1use std::{rc::Rc, sync::Arc};
2
3use super::*;
4use crate::{
5 collections::map::HashMap,
6 state::{PREEXISTING_SNAPSHOT_ID, StateRecord},
7};
8
9pub(super) fn find_record_by_id(
10 head: &Rc<StateRecord>,
11 target: SnapshotId,
12) -> Option<Rc<StateRecord>> {
13 let mut cursor = Some(Rc::clone(head));
14 while let Some(record) = cursor {
15 if !record.is_tombstone() && record.snapshot_id() == target {
16 return Some(record);
17 }
18 cursor = record.next();
19 }
20 None
21}
22
23pub(super) fn find_previous_record(
24 head: &Rc<StateRecord>,
25 base_snapshot_id: SnapshotId,
26 invalid: &SnapshotIdSet,
27) -> (Option<Rc<StateRecord>>, bool) {
28 let mut cursor = Some(Rc::clone(head));
29 let mut best: Option<Rc<StateRecord>> = None;
30 let mut fallback: Option<Rc<StateRecord>> = None;
31 let mut found_base = false;
32
33 while let Some(record) = cursor {
34 if !record.is_tombstone() {
35 let id = record.snapshot_id();
36 let is_valid = id <= base_snapshot_id && !invalid.get(id);
37 if is_valid {
38 found_base = true;
39 let replace = best
40 .as_ref()
41 .map(|current| current.snapshot_id() < id)
42 .unwrap_or(true);
43 if replace {
44 best = Some(record.clone());
45 }
46 }
47 if fallback.is_none() {
48 fallback = Some(record.clone());
49 }
50 }
51 cursor = record.next();
52 }
53
54 (best.or(fallback), found_base)
55}
56
57enum ApplyOperation {
58 PromoteChild {
59 object_id: StateObjectId,
60 state: Arc<dyn StateObject>,
61 writer_id: SnapshotId,
62 },
63 PromoteExisting {
64 object_id: StateObjectId,
65 state: Arc<dyn StateObject>,
66 source_id: SnapshotId,
67 applied: Rc<StateRecord>,
68 },
69 CommitMerged {
70 object_id: StateObjectId,
71 state: Arc<dyn StateObject>,
72 merged: Rc<StateRecord>,
73 applied: Rc<StateRecord>,
74 },
75}
76
77#[allow(clippy::arc_with_non_send_sync)]
88pub struct MutableSnapshot {
89 state: SnapshotState,
90 base_parent_id: SnapshotId,
91 nested_count: Cell<usize>,
92 applied: Cell<bool>,
93}
94
95impl MutableSnapshot {
96 pub(crate) fn from_parts(
97 id: SnapshotId,
98 invalid: SnapshotIdSet,
99 read_observer: Option<ReadObserver>,
100 write_observer: Option<WriteObserver>,
101 base_parent_id: SnapshotId,
102 runtime_tracked: bool,
103 ) -> Arc<Self> {
104 Arc::new(Self {
105 state: SnapshotState::new(id, invalid, read_observer, write_observer, runtime_tracked),
106 base_parent_id,
107 nested_count: Cell::new(0),
108 applied: Cell::new(false),
109 })
110 }
111
112 pub fn new(
114 id: SnapshotId,
115 invalid: SnapshotIdSet,
116 read_observer: Option<ReadObserver>,
117 write_observer: Option<WriteObserver>,
118 base_parent_id: SnapshotId,
119 ) -> Arc<Self> {
120 Self::from_parts(
121 id,
122 invalid,
123 read_observer,
124 write_observer,
125 base_parent_id,
126 false,
127 )
128 }
129
130 fn validate_not_applied(&self) {
131 if self.applied.get() {
132 panic!("Snapshot has already been applied");
133 }
134 }
135
136 fn validate_not_disposed(&self) {
137 if self.state.disposed.get() {
138 panic!("Snapshot has been disposed");
139 }
140 }
141
142 pub fn snapshot_id(&self) -> SnapshotId {
143 self.state.id.get()
144 }
145
146 pub fn invalid(&self) -> SnapshotIdSet {
147 self.state.invalid.borrow().clone()
148 }
149
150 pub fn read_only(&self) -> bool {
151 false
152 }
153
154 pub(crate) fn set_on_dispose<F>(&self, f: F)
155 where
156 F: FnOnce() + 'static,
157 {
158 self.state.set_on_dispose(f);
159 }
160
161 pub fn root_mutable(self: &Arc<Self>) -> Arc<Self> {
162 self.clone()
163 }
164
165 pub fn enter<T>(self: &Arc<Self>, f: impl FnOnce() -> T) -> T {
166 enter_snapshot_scope(AnySnapshot::Mutable(self.clone()), f)
167 }
168
169 pub fn take_nested_snapshot(
170 self: &Arc<Self>,
171 read_observer: Option<ReadObserver>,
172 ) -> Arc<ReadonlySnapshot> {
173 self.validate_not_disposed();
174 self.validate_not_applied();
175
176 let merged_observer =
177 merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
178
179 let nested = ReadonlySnapshot::new(
180 self.state.id.get(),
181 self.state.invalid.borrow().clone(),
182 merged_observer,
183 );
184
185 self.nested_count.set(self.nested_count.get() + 1);
186
187 let parent_weak = Arc::downgrade(self);
188 nested.set_on_dispose(move || {
189 if let Some(parent) = parent_weak.upgrade() {
190 let cur = parent.nested_count.get();
191 if cur > 0 {
192 parent.nested_count.set(cur - 1);
193 }
194 }
195 });
196 nested
197 }
198
199 pub fn has_pending_changes(&self) -> bool {
200 !self.state.modified.borrow().is_empty()
201 }
202
203 pub fn pending_children(&self) -> Vec<SnapshotId> {
204 self.state.pending_children()
205 }
206
207 pub fn has_pending_children(&self) -> bool {
208 self.state.has_pending_children()
209 }
210
211 pub fn dispose(&self) {
212 if !self.state.disposed.get() && self.nested_count.get() == 0 {
213 self.state.dispose();
214 }
215 }
216
217 pub fn record_read(&self, state: &dyn StateObject) {
218 self.state.record_read(state);
219 }
220
221 pub fn record_write(&self, state: Arc<dyn StateObject>) {
222 self.validate_not_applied();
223 self.validate_not_disposed();
224 self.state.record_write(state, self.state.id.get());
225 }
226
227 pub fn close(&self) {
228 self.state.disposed.set(true);
229 }
230
231 pub fn is_disposed(&self) -> bool {
232 self.state.disposed.get()
233 }
234
235 pub fn apply(&self) -> SnapshotApplyResult {
236 if self.state.disposed.get() {
237 return SnapshotApplyResult::Failure;
238 }
239
240 if self.applied.get() {
241 return SnapshotApplyResult::Failure;
242 }
243
244 let modified = self.state.modified.borrow();
245 if modified.is_empty() {
246 self.applied.set(true);
247 self.state.dispose();
248 return SnapshotApplyResult::Success;
249 }
250
251 let this_id = self.state.id.get();
252 let mut modified_objects: Vec<(StateObjectId, Arc<dyn StateObject>, SnapshotId)> =
253 Vec::with_capacity(modified.len());
254 for (&obj_id, (obj, writer_id)) in modified.iter() {
255 modified_objects.push((obj_id, obj.clone(), *writer_id));
256 }
257
258 drop(modified);
259
260 let parent_snapshot = GlobalSnapshot::get_or_create();
261 let parent_snapshot_id = parent_snapshot.snapshot_id();
262 let parent_invalid = parent_snapshot.invalid();
263 drop(parent_snapshot);
264
265 let next_invalid = super::runtime::open_snapshots().clear(parent_snapshot_id);
266 let this_invalid_for_optimistic = self.state.invalid.borrow().clone();
267 let optimistic = super::optimistic_merges(
268 parent_snapshot_id,
269 self.base_parent_id,
270 &modified_objects,
271 &next_invalid,
272 &this_invalid_for_optimistic,
273 );
274
275 let mut operations: Vec<ApplyOperation> = Vec::with_capacity(modified_objects.len());
276
277 for (obj_id, state, writer_id) in &modified_objects {
278 let head = state.first_record();
279 let applied = match find_record_by_id(&head, *writer_id) {
280 Some(record) => record,
281 None => return SnapshotApplyResult::Failure,
282 };
283
284 let Some(current) =
285 crate::state::readable_record_for(&head, parent_snapshot_id, &next_invalid)
286 .or_else(|| state.try_readable_record(parent_snapshot_id, &parent_invalid))
287 else {
288 log::error!(
289 "MutableSnapshot::apply missing parent readable record (object_id={:?}, parent_snapshot_id={})",
290 obj_id,
291 parent_snapshot_id
292 );
293 return SnapshotApplyResult::Failure;
294 };
295 let this_invalid = self.state.invalid.borrow();
296 let (previous_opt, found_base) =
297 find_previous_record(&head, self.base_parent_id, &this_invalid);
298 drop(this_invalid);
299 let Some(previous) = previous_opt else {
300 return SnapshotApplyResult::Failure;
301 };
302
303 if !found_base || previous.snapshot_id() == PREEXISTING_SNAPSHOT_ID {
304 operations.push(ApplyOperation::PromoteChild {
305 object_id: *obj_id,
306 state: state.clone(),
307 writer_id: *writer_id,
308 });
309 continue;
310 }
311
312 if Rc::ptr_eq(¤t, &previous) {
313 operations.push(ApplyOperation::PromoteChild {
314 object_id: *obj_id,
315 state: state.clone(),
316 writer_id: *writer_id,
317 });
318 continue;
319 }
320
321 let merged = if let Some(candidate) = optimistic
322 .as_ref()
323 .and_then(|map| map.get(&(Rc::as_ptr(¤t) as usize)))
324 .cloned()
325 {
326 candidate
327 } else {
328 match state.merge_records(
329 Rc::clone(&previous),
330 Rc::clone(¤t),
331 Rc::clone(&applied),
332 ) {
333 Some(record) => record,
334 None => return SnapshotApplyResult::Failure,
335 }
336 };
337
338 if Rc::ptr_eq(&merged, &applied) {
339 operations.push(ApplyOperation::PromoteChild {
340 object_id: *obj_id,
341 state: state.clone(),
342 writer_id: *writer_id,
343 });
344 } else if Rc::ptr_eq(&merged, ¤t) {
345 operations.push(ApplyOperation::PromoteExisting {
346 object_id: *obj_id,
347 state: state.clone(),
348 source_id: current.snapshot_id(),
349 applied: applied.clone(),
350 });
351 } else {
352 operations.push(ApplyOperation::CommitMerged {
353 object_id: *obj_id,
354 state: state.clone(),
355 merged: merged.clone(),
356 applied: applied.clone(),
357 });
358 }
359 }
360
361 let mut applied_info: Vec<(StateObjectId, Arc<dyn StateObject>, SnapshotId)> =
362 Vec::with_capacity(operations.len());
363
364 for operation in operations {
365 match operation {
366 ApplyOperation::PromoteChild {
367 object_id,
368 state,
369 writer_id,
370 } => {
371 if state.promote_record(writer_id).is_err() {
372 return SnapshotApplyResult::Failure;
373 }
374 let new_head_id = state.first_record().snapshot_id();
375 applied_info.push((object_id, state, new_head_id));
376 }
377 ApplyOperation::PromoteExisting {
378 object_id,
379 state,
380 source_id,
381 applied,
382 } => {
383 if state.promote_record(source_id).is_err() {
384 return SnapshotApplyResult::Failure;
385 }
386 applied.set_tombstone(true);
387 applied.clear_value();
388 let new_head_id = state.first_record().snapshot_id();
389 applied_info.push((object_id, state, new_head_id));
390 }
391 ApplyOperation::CommitMerged {
392 object_id,
393 state,
394 merged,
395 applied,
396 } => {
397 let Ok(new_head_id) = state.commit_merged_record(merged) else {
398 return SnapshotApplyResult::Failure;
399 };
400 applied.set_tombstone(true);
401 applied.clear_value();
402 applied_info.push((object_id, state, new_head_id));
403 }
404 }
405 }
406
407 for (obj_id, _, head_id) in &applied_info {
408 super::set_last_write(*obj_id, *head_id);
409 }
410
411 self.applied.set(true);
412 self.state.dispose();
413
414 for (_, state, _) in &applied_info {
415 super::EXTRA_STATE_OBJECTS.with(|cell| {
416 cell.borrow_mut().add_trait_object(state);
417 });
418 }
419
420 let observer_states: Vec<Arc<dyn StateObject>> = applied_info
421 .iter()
422 .map(|(_, state, _)| state.clone())
423 .collect();
424 super::notify_apply_observers(&observer_states, this_id);
425 SnapshotApplyResult::Success
426 }
427
428 pub fn take_nested_mutable_snapshot(
429 self: &Arc<Self>,
430 read_observer: Option<ReadObserver>,
431 write_observer: Option<WriteObserver>,
432 ) -> Arc<NestedMutableSnapshot> {
433 self.validate_not_disposed();
434 self.validate_not_applied();
435
436 allocate_nested_mutable_snapshot(self, Arc::downgrade(self), read_observer, write_observer)
437 }
438
439 pub(crate) fn merge_child_modifications(
440 &self,
441 child_modified: &HashMap<StateObjectId, (Arc<dyn StateObject>, SnapshotId)>,
442 ) -> Result<(), ()> {
443 {
444 let parent_mod = self.state.modified.borrow();
445 for key in child_modified.keys() {
446 if parent_mod.contains_key(key) {
447 return Err(());
448 }
449 }
450 }
451
452 let mut parent_mod = self.state.modified.borrow_mut();
453 for (key, value) in child_modified.iter() {
454 parent_mod.entry(*key).or_insert_with(|| value.clone());
455 }
456 Ok(())
457 }
458}
459
460impl NestedMutableHost for MutableSnapshot {
461 fn snapshot_state(&self) -> &SnapshotState {
462 &self.state
463 }
464
465 fn nested_count(&self) -> &Cell<usize> {
466 &self.nested_count
467 }
468}
469
470#[cfg(test)]
471impl MutableSnapshot {
472 pub(crate) fn debug_modified_objects(
473 &self,
474 ) -> Vec<(StateObjectId, Arc<dyn StateObject>, SnapshotId)> {
475 let modified = self.state.modified.borrow();
476 modified
477 .iter()
478 .map(|(&obj_id, (state, writer_id))| (obj_id, state.clone(), *writer_id))
479 .collect()
480 }
481
482 pub(crate) fn debug_base_parent_id(&self) -> SnapshotId {
483 self.base_parent_id
484 }
485}
486
487#[cfg(test)]
488mod tests {
489 use std::sync::Arc;
490
491 use super::*;
492 use crate::{
493 snapshot_v2::runtime::TestRuntimeGuard,
494 state::{NeverEqual, SnapshotMutableState, StateObject},
495 };
496
497 fn reset_runtime() -> TestRuntimeGuard {
498 reset_runtime_for_tests()
499 }
500
501 fn new_state(initial: i32) -> Arc<SnapshotMutableState<i32>> {
502 SnapshotMutableState::new_in_arc(initial, Arc::new(NeverEqual))
503 }
504
505 struct MockStateObject;
506
507 fn mock_state_record() -> Rc<crate::state::StateRecord> {
508 crate::state::StateRecord::new(crate::state::PREEXISTING_SNAPSHOT_ID, (), None)
509 }
510
511 impl StateObject for MockStateObject {
512 fn object_id(&self) -> crate::state::ObjectId {
513 crate::state::ObjectId(0)
514 }
515
516 fn first_record(&self) -> Rc<crate::state::StateRecord> {
517 mock_state_record()
518 }
519
520 fn try_readable_record(
521 &self,
522 snapshot_id: crate::snapshot_id_set::SnapshotId,
523 invalid: &SnapshotIdSet,
524 ) -> Option<Rc<crate::state::StateRecord>> {
525 Some(self.readable_record(snapshot_id, invalid))
526 }
527
528 fn readable_record(
529 &self,
530 _snapshot_id: crate::snapshot_id_set::SnapshotId,
531 _invalid: &SnapshotIdSet,
532 ) -> Rc<crate::state::StateRecord> {
533 mock_state_record()
534 }
535
536 fn prepend_state_record(&self, _record: Rc<crate::state::StateRecord>) {}
537
538 fn promote_record(
539 &self,
540 _child_id: crate::snapshot_id_set::SnapshotId,
541 ) -> Result<(), &'static str> {
542 Ok(())
543 }
544
545 fn as_any(&self) -> &dyn std::any::Any {
546 self
547 }
548 }
549
550 struct MissingParentReadableStateObject {
551 record: Rc<crate::state::StateRecord>,
552 }
553
554 impl MissingParentReadableStateObject {
555 fn new(writer_id: SnapshotId) -> Self {
556 Self {
557 record: crate::state::StateRecord::new(writer_id, (), None),
558 }
559 }
560 }
561
562 impl StateObject for MissingParentReadableStateObject {
563 fn object_id(&self) -> crate::state::ObjectId {
564 crate::state::ObjectId(10_001)
565 }
566
567 fn first_record(&self) -> Rc<crate::state::StateRecord> {
568 Rc::clone(&self.record)
569 }
570
571 fn try_readable_record(
572 &self,
573 _: SnapshotId,
574 _: &SnapshotIdSet,
575 ) -> Option<Rc<crate::state::StateRecord>> {
576 None
577 }
578
579 fn readable_record(
580 &self,
581 _: SnapshotId,
582 _: &SnapshotIdSet,
583 ) -> Rc<crate::state::StateRecord> {
584 panic!("apply must use a fallible parent readable-record lookup")
585 }
586
587 fn prepend_state_record(&self, _record: Rc<crate::state::StateRecord>) {}
588
589 fn promote_record(
590 &self,
591 _child_id: crate::snapshot_id_set::SnapshotId,
592 ) -> Result<(), &'static str> {
593 Ok(())
594 }
595
596 fn as_any(&self) -> &dyn std::any::Any {
597 self
598 }
599 }
600
601 #[test]
602 fn test_mutable_snapshot_creation() {
603 let _guard = reset_runtime();
604 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
605 assert_eq!(snapshot.snapshot_id(), 1);
606 assert!(!snapshot.read_only());
607 assert!(!snapshot.is_disposed());
608 assert!(!snapshot.applied.get());
609 }
610
611 #[test]
612 fn test_mutable_snapshot_no_pending_changes_initially() {
613 let _guard = reset_runtime();
614 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
615 assert!(!snapshot.has_pending_changes());
616 }
617
618 #[test]
619 fn test_mutable_snapshot_enter() {
620 let _guard = reset_runtime();
621 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
622
623 set_current_snapshot(None);
624 assert!(current_snapshot().is_none());
625
626 snapshot.enter(|| {
627 let current = current_snapshot();
628 assert!(current.is_some());
629 assert_eq!(current.unwrap().snapshot_id(), 1);
630 });
631
632 assert!(current_snapshot().is_none());
633 }
634
635 #[test]
636 fn test_mutable_snapshot_read_observer() {
637 let _guard = reset_runtime();
638 use std::sync::{Arc as StdArc, Mutex};
639
640 let read_count = StdArc::new(Mutex::new(0));
641 let read_count_clone = read_count.clone();
642
643 let observer = Arc::new(move |_: &dyn StateObject| {
644 *read_count_clone.lock().unwrap() += 1;
645 });
646
647 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), Some(observer), None, 0);
648 let mock_state = MockStateObject;
649
650 snapshot.record_read(&mock_state);
651 snapshot.record_read(&mock_state);
652
653 assert_eq!(*read_count.lock().unwrap(), 2);
654 }
655
656 #[test]
657 fn test_mutable_snapshot_write_observer() {
658 let _guard = reset_runtime();
659 use std::sync::{Arc as StdArc, Mutex};
660
661 let write_count = StdArc::new(Mutex::new(0));
662 let write_count_clone = write_count.clone();
663
664 let observer = Arc::new(move |_: &dyn StateObject| {
665 *write_count_clone.lock().unwrap() += 1;
666 });
667
668 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, Some(observer), 0);
669 let mock_state = Arc::new(MockStateObject);
670
671 snapshot.record_write(mock_state.clone());
672 snapshot.record_write(mock_state.clone());
673
674 assert_eq!(*write_count.lock().unwrap(), 1);
675 }
676
677 #[test]
678 fn test_mutable_snapshot_apply_empty() {
679 let _guard = reset_runtime();
680 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
681 let result = snapshot.apply();
682 assert!(result.is_success());
683 assert!(snapshot.applied.get());
684 }
685
686 #[test]
687 fn mutable_apply_returns_failure_when_parent_readable_record_is_missing() {
688 let _guard = reset_runtime();
689 let snapshot = MutableSnapshot::new(7, SnapshotIdSet::new(), None, None, 1);
690 let state = Arc::new(MissingParentReadableStateObject::new(
691 snapshot.snapshot_id(),
692 ));
693
694 snapshot.record_write(state);
695
696 let result = snapshot.apply();
697
698 assert!(result.is_failure());
699 }
700
701 #[test]
702 fn test_mutable_snapshot_apply_twice_fails() {
703 let _guard = reset_runtime();
704 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
705 snapshot.apply().check();
706
707 let result = snapshot.apply();
708 assert!(result.is_failure());
709 }
710
711 #[test]
712 fn test_mutable_snapshot_nested_readonly() {
713 let _guard = reset_runtime();
714 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
715 let nested = parent.take_nested_snapshot(None);
716
717 assert_eq!(nested.snapshot_id(), 1);
718 assert!(nested.read_only());
719 assert_eq!(parent.nested_count.get(), 1);
720 }
721
722 #[test]
723 fn test_mutable_snapshot_nested_mutable() {
724 let _guard = reset_runtime();
725 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
726 let nested = parent.take_nested_mutable_snapshot(None, None);
727
728 assert!(nested.snapshot_id() > parent.snapshot_id());
729 assert!(!nested.read_only());
730 assert_eq!(parent.nested_count.get(), 1);
731 }
732
733 #[test]
734 fn test_mutable_snapshot_nested_mutable_dispose_clears_invalid() {
735 let _guard = reset_runtime();
736 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
737 let nested = parent.take_nested_mutable_snapshot(None, None);
738
739 let child_id = nested.snapshot_id();
740 assert!(parent.state.invalid.borrow().get(child_id));
741
742 nested.dispose();
743
744 assert_eq!(parent.nested_count.get(), 0);
745 assert!(!parent.state.invalid.borrow().get(child_id));
746 }
747
748 #[test]
749 fn test_mutable_snapshot_nested_dispose() {
750 let _guard = reset_runtime();
751 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
752 let nested = parent.take_nested_snapshot(None);
753
754 assert_eq!(parent.nested_count.get(), 1);
755
756 nested.dispose();
757 assert_eq!(parent.nested_count.get(), 0);
758 }
759
760 #[test]
761 #[should_panic(expected = "Snapshot has already been applied")]
762 fn test_mutable_snapshot_write_after_apply_panics() {
763 let _guard = reset_runtime();
764 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
765 snapshot.apply().check();
766
767 let mock_state = Arc::new(MockStateObject);
768 snapshot.record_write(mock_state);
769 }
770
771 #[test]
772 #[should_panic(expected = "Snapshot has been disposed")]
773 fn test_mutable_snapshot_write_after_dispose_panics() {
774 let _guard = reset_runtime();
775 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
776 snapshot.dispose();
777
778 let mock_state = Arc::new(MockStateObject);
779 snapshot.record_write(mock_state);
780 }
781
782 #[test]
783 fn test_mutable_snapshot_dispose() {
784 let _guard = reset_runtime();
785 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
786 assert!(!snapshot.is_disposed());
787
788 snapshot.dispose();
789 assert!(snapshot.is_disposed());
790 }
791
792 #[test]
793 fn test_mutable_snapshot_apply_observer() {
794 let _guard = reset_runtime();
795 use std::sync::{Arc as StdArc, Mutex};
796
797 let applied_count = StdArc::new(Mutex::new(0));
798 let applied_count_clone = applied_count.clone();
799
800 let observer = Rc::new(
801 move |_modified: &[Arc<dyn StateObject>], _snapshot_id: SnapshotId| {
802 *applied_count_clone.lock().unwrap() += 1;
803 },
804 );
805
806 let _handle = register_apply_observer(observer);
807
808 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
809 let state = new_state(0);
810
811 snapshot.enter(|| state.set(10));
812 snapshot.apply().check();
813
814 assert_eq!(*applied_count.lock().unwrap(), 1);
815 }
816
817 #[test]
818 fn test_mutable_conflict_detection_same_object() {
819 let _guard = reset_runtime();
820 let global = GlobalSnapshot::get_or_create();
821 let state = new_state(0);
822
823 let s1 = global.take_nested_mutable_snapshot(None, None);
824 s1.enter(|| state.set(1));
825
826 let s2 = global.take_nested_mutable_snapshot(None, None);
827 s2.enter(|| state.set(2));
828
829 assert!(s1.apply().is_success());
830 assert!(s2.apply().is_failure());
831 }
832
833 #[test]
834 fn test_mutable_no_conflict_different_objects() {
835 let _guard = reset_runtime();
836 let global = GlobalSnapshot::get_or_create();
837 let state1 = new_state(0);
838 let state2 = new_state(0);
839
840 let s1 = global.take_nested_mutable_snapshot(None, None);
841 s1.enter(|| state1.set(10));
842
843 let s2 = global.take_nested_mutable_snapshot(None, None);
844 s2.enter(|| state2.set(20));
845
846 assert!(s1.apply().is_success());
847 assert!(s2.apply().is_success());
848 }
849}