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