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::sync::Arc;
569
570 fn reset_runtime() -> TestRuntimeGuard {
571 reset_runtime_for_tests()
572 }
573
574 fn new_state(initial: i32) -> Arc<SnapshotMutableState<i32>> {
575 SnapshotMutableState::new_in_arc(initial, Arc::new(NeverEqual))
576 }
577
578 struct MockStateObject;
580
581 fn mock_state_record() -> Rc<crate::state::StateRecord> {
582 crate::state::StateRecord::new(crate::state::PREEXISTING_SNAPSHOT_ID, (), None)
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 mock_state_record()
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 mock_state_record()
600 }
601
602 fn prepend_state_record(&self, _record: Rc<crate::state::StateRecord>) {}
603
604 fn promote_record(
605 &self,
606 _child_id: crate::snapshot_id_set::SnapshotId,
607 ) -> Result<(), &'static str> {
608 Ok(())
609 }
610
611 fn as_any(&self) -> &dyn std::any::Any {
612 self
613 }
614 }
615
616 #[test]
617 fn test_mutable_snapshot_creation() {
618 let _guard = reset_runtime();
619 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
620 assert_eq!(snapshot.snapshot_id(), 1);
621 assert!(!snapshot.read_only());
622 assert!(!snapshot.is_disposed());
623 assert!(!snapshot.applied.get());
624 }
625
626 #[test]
627 fn test_mutable_snapshot_no_pending_changes_initially() {
628 let _guard = reset_runtime();
629 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
630 assert!(!snapshot.has_pending_changes());
631 }
632
633 #[test]
634 fn test_mutable_snapshot_enter() {
635 let _guard = reset_runtime();
636 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
637
638 set_current_snapshot(None);
639 assert!(current_snapshot().is_none());
640
641 snapshot.enter(|| {
642 let current = current_snapshot();
643 assert!(current.is_some());
644 assert_eq!(current.unwrap().snapshot_id(), 1);
645 });
646
647 assert!(current_snapshot().is_none());
648 }
649
650 #[test]
651 fn test_mutable_snapshot_read_observer() {
652 let _guard = reset_runtime();
653 use std::sync::{Arc as StdArc, Mutex};
654
655 let read_count = StdArc::new(Mutex::new(0));
656 let read_count_clone = read_count.clone();
657
658 let observer = Arc::new(move |_: &dyn StateObject| {
659 *read_count_clone.lock().unwrap() += 1;
660 });
661
662 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), Some(observer), None, 0);
663 let mock_state = MockStateObject;
664
665 snapshot.record_read(&mock_state);
666 snapshot.record_read(&mock_state);
667
668 assert_eq!(*read_count.lock().unwrap(), 2);
669 }
670
671 #[test]
672 fn test_mutable_snapshot_write_observer() {
673 let _guard = reset_runtime();
674 use std::sync::{Arc as StdArc, Mutex};
675
676 let write_count = StdArc::new(Mutex::new(0));
677 let write_count_clone = write_count.clone();
678
679 let observer = Arc::new(move |_: &dyn StateObject| {
680 *write_count_clone.lock().unwrap() += 1;
681 });
682
683 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, Some(observer), 0);
684 let mock_state = Arc::new(MockStateObject);
685
686 snapshot.record_write(mock_state.clone());
687 snapshot.record_write(mock_state.clone()); assert!(*write_count.lock().unwrap() >= 1);
692 }
693
694 #[test]
695 fn test_mutable_snapshot_apply_empty() {
696 let _guard = reset_runtime();
697 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
698 let result = snapshot.apply();
699 assert!(result.is_success());
700 assert!(snapshot.applied.get());
701 }
702
703 #[test]
704 fn test_mutable_snapshot_apply_twice_fails() {
705 let _guard = reset_runtime();
706 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
707 snapshot.apply().check();
708
709 let result = snapshot.apply();
710 assert!(result.is_failure());
711 }
712
713 #[test]
714 fn test_mutable_snapshot_nested_readonly() {
715 let _guard = reset_runtime();
716 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
717 let nested = parent.take_nested_snapshot(None);
718
719 assert_eq!(nested.snapshot_id(), 1);
720 assert!(nested.read_only());
721 assert_eq!(parent.nested_count.get(), 1);
722 }
723
724 #[test]
725 fn test_mutable_snapshot_nested_mutable() {
726 let _guard = reset_runtime();
727 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
728 let nested = parent.take_nested_mutable_snapshot(None, None);
729
730 assert!(nested.snapshot_id() > parent.snapshot_id());
731 assert!(!nested.read_only());
732 assert_eq!(parent.nested_count.get(), 1);
733 }
734
735 #[test]
736 fn test_mutable_snapshot_nested_mutable_dispose_clears_invalid() {
737 let _guard = reset_runtime();
738 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
739 let nested = parent.take_nested_mutable_snapshot(None, None);
740
741 let child_id = nested.snapshot_id();
742 assert!(parent.state.invalid.borrow().get(child_id));
743
744 nested.dispose();
745
746 assert_eq!(parent.nested_count.get(), 0);
747 assert!(!parent.state.invalid.borrow().get(child_id));
748 }
749
750 #[test]
751 fn test_mutable_snapshot_nested_dispose() {
752 let _guard = reset_runtime();
753 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
754 let nested = parent.take_nested_snapshot(None);
755
756 assert_eq!(parent.nested_count.get(), 1);
757
758 nested.dispose();
759 assert_eq!(parent.nested_count.get(), 0);
760 }
761
762 #[test]
763 #[should_panic(expected = "Snapshot has already been applied")]
764 fn test_mutable_snapshot_write_after_apply_panics() {
765 let _guard = reset_runtime();
766 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
767 snapshot.apply().check();
768
769 let mock_state = Arc::new(MockStateObject);
770 snapshot.record_write(mock_state);
771 }
772
773 #[test]
774 #[should_panic(expected = "Snapshot has been disposed")]
775 fn test_mutable_snapshot_write_after_dispose_panics() {
776 let _guard = reset_runtime();
777 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
778 snapshot.dispose();
779
780 let mock_state = Arc::new(MockStateObject);
781 snapshot.record_write(mock_state);
782 }
783
784 #[test]
785 fn test_mutable_snapshot_dispose() {
786 let _guard = reset_runtime();
787 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
788 assert!(!snapshot.is_disposed());
789
790 snapshot.dispose();
791 assert!(snapshot.is_disposed());
792 }
793
794 #[test]
795 fn test_mutable_snapshot_apply_observer() {
796 let _guard = reset_runtime();
797 use std::sync::{Arc as StdArc, Mutex};
798
799 let applied_count = StdArc::new(Mutex::new(0));
800 let applied_count_clone = applied_count.clone();
801
802 let observer = Rc::new(
803 move |_modified: &[Arc<dyn StateObject>], _snapshot_id: SnapshotId| {
804 *applied_count_clone.lock().unwrap() += 1;
805 },
806 );
807
808 let _handle = register_apply_observer(observer);
809
810 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
811 let state = new_state(0);
812
813 snapshot.enter(|| state.set(10));
814 snapshot.apply().check();
815
816 assert_eq!(*applied_count.lock().unwrap(), 1);
817 }
818
819 #[test]
820 fn test_mutable_conflict_detection_same_object() {
821 let _guard = reset_runtime();
822 let global = GlobalSnapshot::get_or_create();
823 let state = new_state(0);
824
825 let s1 = global.take_nested_mutable_snapshot(None, None);
826 s1.enter(|| state.set(1));
827
828 let s2 = global.take_nested_mutable_snapshot(None, None);
829 s2.enter(|| state.set(2));
830
831 assert!(s1.apply().is_success());
832 assert!(s2.apply().is_failure());
833 }
834
835 #[test]
836 fn test_mutable_no_conflict_different_objects() {
837 let _guard = reset_runtime();
838 let global = GlobalSnapshot::get_or_create();
839 let state1 = new_state(0);
840 let state2 = new_state(0);
841
842 let s1 = global.take_nested_mutable_snapshot(None, None);
843 s1.enter(|| state1.set(10));
844
845 let s2 = global.take_nested_mutable_snapshot(None, None);
846 s2.enter(|| state2.set(20));
847
848 assert!(s1.apply().is_success());
849 assert!(s2.apply().is_success());
850 }
851}