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 enter_snapshot_scope(AnySnapshot::Mutable(self.clone()), f)
185 }
186
187 pub fn take_nested_snapshot(
188 self: &Arc<Self>,
189 read_observer: Option<ReadObserver>,
190 ) -> Arc<ReadonlySnapshot> {
191 self.validate_not_disposed();
192 self.validate_not_applied();
193
194 let merged_observer =
195 merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
196
197 let nested = ReadonlySnapshot::new(
199 self.state.id.get(),
200 self.state.invalid.borrow().clone(),
201 merged_observer,
202 );
203
204 self.nested_count.set(self.nested_count.get() + 1);
205
206 let parent_weak = Arc::downgrade(self);
208 nested.set_on_dispose(move || {
209 if let Some(parent) = parent_weak.upgrade() {
210 let cur = parent.nested_count.get();
211 if cur > 0 {
212 parent.nested_count.set(cur - 1);
213 }
214 }
215 });
216 nested
217 }
218
219 pub fn has_pending_changes(&self) -> bool {
220 !self.state.modified.borrow().is_empty()
221 }
222
223 pub fn pending_children(&self) -> Vec<SnapshotId> {
224 self.state.pending_children()
225 }
226
227 pub fn has_pending_children(&self) -> bool {
228 self.state.has_pending_children()
229 }
230
231 pub fn dispose(&self) {
232 if !self.state.disposed.get() && self.nested_count.get() == 0 {
233 self.state.dispose();
234 }
235 }
236
237 pub fn record_read(&self, state: &dyn StateObject) {
238 self.state.record_read(state);
239 }
240
241 pub fn record_write(&self, state: Arc<dyn StateObject>) {
242 self.validate_not_applied();
243 self.validate_not_disposed();
244 self.state.record_write(state, self.state.id.get());
245 }
246
247 pub fn close(&self) {
248 self.state.disposed.set(true);
249 }
250
251 pub fn is_disposed(&self) -> bool {
252 self.state.disposed.get()
253 }
254
255 pub fn apply(&self) -> SnapshotApplyResult {
256 if self.state.disposed.get() {
258 return SnapshotApplyResult::Failure;
259 }
260
261 if self.applied.get() {
262 return SnapshotApplyResult::Failure;
263 }
264
265 let modified = self.state.modified.borrow();
266 if modified.is_empty() {
267 self.applied.set(true);
269 self.state.dispose();
270 return SnapshotApplyResult::Success;
271 }
272
273 let this_id = self.state.id.get();
274 let mut modified_objects: Vec<(StateObjectId, Arc<dyn StateObject>, SnapshotId)> =
275 Vec::with_capacity(modified.len());
276 for (&obj_id, (obj, writer_id)) in modified.iter() {
277 modified_objects.push((obj_id, obj.clone(), *writer_id));
278 }
279
280 drop(modified);
281
282 let parent_snapshot = GlobalSnapshot::get_or_create();
283 let parent_snapshot_id = parent_snapshot.snapshot_id();
284 let parent_invalid = parent_snapshot.invalid();
285 drop(parent_snapshot);
286
287 let next_invalid = super::runtime::open_snapshots().clear(parent_snapshot_id);
288 let this_invalid_for_optimistic = self.state.invalid.borrow().clone();
289 let optimistic = super::optimistic_merges(
290 parent_snapshot_id,
291 self.base_parent_id,
292 &modified_objects,
293 &next_invalid,
294 &this_invalid_for_optimistic,
295 );
296
297 let mut operations: Vec<ApplyOperation> = Vec::with_capacity(modified_objects.len());
298
299 for (obj_id, state, writer_id) in &modified_objects {
300 let head = state.first_record();
301 let applied = match find_record_by_id(&head, *writer_id) {
302 Some(record) => record,
303 None => return SnapshotApplyResult::Failure,
304 };
305
306 let Some(current) =
307 crate::state::readable_record_for(&head, parent_snapshot_id, &next_invalid)
308 .or_else(|| state.try_readable_record(parent_snapshot_id, &parent_invalid))
309 else {
310 log::error!(
311 "MutableSnapshot::apply missing parent readable record (object_id={:?}, parent_snapshot_id={})",
312 obj_id,
313 parent_snapshot_id
314 );
315 return SnapshotApplyResult::Failure;
316 };
317 let this_invalid = self.state.invalid.borrow();
320 let (previous_opt, found_base) =
321 find_previous_record(&head, self.base_parent_id, &this_invalid);
322 drop(this_invalid);
323 let Some(previous) = previous_opt else {
324 return SnapshotApplyResult::Failure;
325 };
326
327 if !found_base || previous.snapshot_id() == PREEXISTING_SNAPSHOT_ID {
328 operations.push(ApplyOperation::PromoteChild {
329 object_id: *obj_id,
330 state: state.clone(),
331 writer_id: *writer_id,
332 });
333 continue;
334 }
335
336 if Rc::ptr_eq(¤t, &previous) {
337 operations.push(ApplyOperation::PromoteChild {
338 object_id: *obj_id,
339 state: state.clone(),
340 writer_id: *writer_id,
341 });
342 continue;
343 }
344
345 let merged = if let Some(candidate) = optimistic
346 .as_ref()
347 .and_then(|map| map.get(&(Rc::as_ptr(¤t) as usize)))
348 .cloned()
349 {
350 candidate
351 } else {
352 match state.merge_records(
353 Rc::clone(&previous),
354 Rc::clone(¤t),
355 Rc::clone(&applied),
356 ) {
357 Some(record) => record,
358 None => return SnapshotApplyResult::Failure,
359 }
360 };
361
362 if Rc::ptr_eq(&merged, &applied) {
363 operations.push(ApplyOperation::PromoteChild {
364 object_id: *obj_id,
365 state: state.clone(),
366 writer_id: *writer_id,
367 });
368 } else if Rc::ptr_eq(&merged, ¤t) {
369 operations.push(ApplyOperation::PromoteExisting {
370 object_id: *obj_id,
371 state: state.clone(),
372 source_id: current.snapshot_id(),
373 applied: applied.clone(),
374 });
375 } else {
376 operations.push(ApplyOperation::CommitMerged {
377 object_id: *obj_id,
378 state: state.clone(),
379 merged: merged.clone(),
380 applied: applied.clone(),
381 });
382 }
383 }
384
385 let mut applied_info: Vec<(StateObjectId, Arc<dyn StateObject>, SnapshotId)> =
386 Vec::with_capacity(operations.len());
387
388 for operation in operations {
389 match operation {
390 ApplyOperation::PromoteChild {
391 object_id,
392 state,
393 writer_id,
394 } => {
395 if state.promote_record(writer_id).is_err() {
396 return SnapshotApplyResult::Failure;
397 }
398 let new_head_id = state.first_record().snapshot_id();
399 applied_info.push((object_id, state, new_head_id));
400 }
401 ApplyOperation::PromoteExisting {
402 object_id,
403 state,
404 source_id,
405 applied,
406 } => {
407 if state.promote_record(source_id).is_err() {
408 return SnapshotApplyResult::Failure;
409 }
410 applied.set_tombstone(true);
411 applied.clear_value();
412 let new_head_id = state.first_record().snapshot_id();
413 applied_info.push((object_id, state, new_head_id));
414 }
415 ApplyOperation::CommitMerged {
416 object_id,
417 state,
418 merged,
419 applied,
420 } => {
421 let Ok(new_head_id) = state.commit_merged_record(merged) else {
422 return SnapshotApplyResult::Failure;
423 };
424 applied.set_tombstone(true);
425 applied.clear_value();
426 applied_info.push((object_id, state, new_head_id));
427 }
428 }
429 }
430
431 for (obj_id, _, head_id) in &applied_info {
432 super::set_last_write(*obj_id, *head_id);
433 }
434
435 self.applied.set(true);
436 self.state.dispose();
437
438 for (_, state, _) in &applied_info {
443 super::EXTRA_STATE_OBJECTS.with(|cell| {
444 cell.borrow_mut().add_trait_object(state);
445 });
446 }
447
448 let observer_states: Vec<Arc<dyn StateObject>> = applied_info
449 .iter()
450 .map(|(_, state, _)| state.clone())
451 .collect();
452 super::notify_apply_observers(&observer_states, this_id);
453 SnapshotApplyResult::Success
454 }
455
456 pub fn take_nested_mutable_snapshot(
457 self: &Arc<Self>,
458 read_observer: Option<ReadObserver>,
459 write_observer: Option<WriteObserver>,
460 ) -> Arc<NestedMutableSnapshot> {
461 self.validate_not_disposed();
462 self.validate_not_applied();
463
464 let merged_read =
465 merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
466 let merged_write =
467 merge_write_observers(write_observer, self.state.write_observer.borrow().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 try_readable_record(
595 &self,
596 snapshot_id: crate::snapshot_id_set::SnapshotId,
597 invalid: &SnapshotIdSet,
598 ) -> Option<Rc<crate::state::StateRecord>> {
599 Some(self.readable_record(snapshot_id, invalid))
600 }
601
602 fn readable_record(
603 &self,
604 _snapshot_id: crate::snapshot_id_set::SnapshotId,
605 _invalid: &SnapshotIdSet,
606 ) -> Rc<crate::state::StateRecord> {
607 mock_state_record()
608 }
609
610 fn prepend_state_record(&self, _record: Rc<crate::state::StateRecord>) {}
611
612 fn promote_record(
613 &self,
614 _child_id: crate::snapshot_id_set::SnapshotId,
615 ) -> Result<(), &'static str> {
616 Ok(())
617 }
618
619 fn as_any(&self) -> &dyn std::any::Any {
620 self
621 }
622 }
623
624 struct MissingParentReadableStateObject {
625 record: Rc<crate::state::StateRecord>,
626 }
627
628 impl MissingParentReadableStateObject {
629 fn new(writer_id: SnapshotId) -> Self {
630 Self {
631 record: crate::state::StateRecord::new(writer_id, (), None),
632 }
633 }
634 }
635
636 impl StateObject for MissingParentReadableStateObject {
637 fn object_id(&self) -> crate::state::ObjectId {
638 crate::state::ObjectId(10_001)
639 }
640
641 fn first_record(&self) -> Rc<crate::state::StateRecord> {
642 Rc::clone(&self.record)
643 }
644
645 fn try_readable_record(
646 &self,
647 _: SnapshotId,
648 _: &SnapshotIdSet,
649 ) -> Option<Rc<crate::state::StateRecord>> {
650 None
651 }
652
653 fn readable_record(
654 &self,
655 _: SnapshotId,
656 _: &SnapshotIdSet,
657 ) -> Rc<crate::state::StateRecord> {
658 panic!("apply must use a fallible parent readable-record lookup")
659 }
660
661 fn prepend_state_record(&self, _record: Rc<crate::state::StateRecord>) {}
662
663 fn promote_record(
664 &self,
665 _child_id: crate::snapshot_id_set::SnapshotId,
666 ) -> Result<(), &'static str> {
667 Ok(())
668 }
669
670 fn as_any(&self) -> &dyn std::any::Any {
671 self
672 }
673 }
674
675 #[test]
676 fn test_mutable_snapshot_creation() {
677 let _guard = reset_runtime();
678 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
679 assert_eq!(snapshot.snapshot_id(), 1);
680 assert!(!snapshot.read_only());
681 assert!(!snapshot.is_disposed());
682 assert!(!snapshot.applied.get());
683 }
684
685 #[test]
686 fn test_mutable_snapshot_no_pending_changes_initially() {
687 let _guard = reset_runtime();
688 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
689 assert!(!snapshot.has_pending_changes());
690 }
691
692 #[test]
693 fn test_mutable_snapshot_enter() {
694 let _guard = reset_runtime();
695 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
696
697 set_current_snapshot(None);
698 assert!(current_snapshot().is_none());
699
700 snapshot.enter(|| {
701 let current = current_snapshot();
702 assert!(current.is_some());
703 assert_eq!(current.unwrap().snapshot_id(), 1);
704 });
705
706 assert!(current_snapshot().is_none());
707 }
708
709 #[test]
710 fn test_mutable_snapshot_read_observer() {
711 let _guard = reset_runtime();
712 use std::sync::{Arc as StdArc, Mutex};
713
714 let read_count = StdArc::new(Mutex::new(0));
715 let read_count_clone = read_count.clone();
716
717 let observer = Arc::new(move |_: &dyn StateObject| {
718 *read_count_clone.lock().unwrap() += 1;
719 });
720
721 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), Some(observer), None, 0);
722 let mock_state = MockStateObject;
723
724 snapshot.record_read(&mock_state);
725 snapshot.record_read(&mock_state);
726
727 assert_eq!(*read_count.lock().unwrap(), 2);
728 }
729
730 #[test]
731 fn test_mutable_snapshot_write_observer() {
732 let _guard = reset_runtime();
733 use std::sync::{Arc as StdArc, Mutex};
734
735 let write_count = StdArc::new(Mutex::new(0));
736 let write_count_clone = write_count.clone();
737
738 let observer = Arc::new(move |_: &dyn StateObject| {
739 *write_count_clone.lock().unwrap() += 1;
740 });
741
742 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, Some(observer), 0);
743 let mock_state = Arc::new(MockStateObject);
744
745 snapshot.record_write(mock_state.clone());
746 snapshot.record_write(mock_state.clone()); assert_eq!(*write_count.lock().unwrap(), 1);
749 }
750
751 #[test]
752 fn test_mutable_snapshot_apply_empty() {
753 let _guard = reset_runtime();
754 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
755 let result = snapshot.apply();
756 assert!(result.is_success());
757 assert!(snapshot.applied.get());
758 }
759
760 #[test]
761 fn mutable_apply_returns_failure_when_parent_readable_record_is_missing() {
762 let _guard = reset_runtime();
763 let snapshot = MutableSnapshot::new(7, SnapshotIdSet::new(), None, None, 1);
764 let state = Arc::new(MissingParentReadableStateObject::new(
765 snapshot.snapshot_id(),
766 ));
767
768 snapshot.record_write(state);
769
770 let result = snapshot.apply();
771
772 assert!(result.is_failure());
773 }
774
775 #[test]
776 fn test_mutable_snapshot_apply_twice_fails() {
777 let _guard = reset_runtime();
778 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
779 snapshot.apply().check();
780
781 let result = snapshot.apply();
782 assert!(result.is_failure());
783 }
784
785 #[test]
786 fn test_mutable_snapshot_nested_readonly() {
787 let _guard = reset_runtime();
788 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
789 let nested = parent.take_nested_snapshot(None);
790
791 assert_eq!(nested.snapshot_id(), 1);
792 assert!(nested.read_only());
793 assert_eq!(parent.nested_count.get(), 1);
794 }
795
796 #[test]
797 fn test_mutable_snapshot_nested_mutable() {
798 let _guard = reset_runtime();
799 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
800 let nested = parent.take_nested_mutable_snapshot(None, None);
801
802 assert!(nested.snapshot_id() > parent.snapshot_id());
803 assert!(!nested.read_only());
804 assert_eq!(parent.nested_count.get(), 1);
805 }
806
807 #[test]
808 fn test_mutable_snapshot_nested_mutable_dispose_clears_invalid() {
809 let _guard = reset_runtime();
810 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
811 let nested = parent.take_nested_mutable_snapshot(None, None);
812
813 let child_id = nested.snapshot_id();
814 assert!(parent.state.invalid.borrow().get(child_id));
815
816 nested.dispose();
817
818 assert_eq!(parent.nested_count.get(), 0);
819 assert!(!parent.state.invalid.borrow().get(child_id));
820 }
821
822 #[test]
823 fn test_mutable_snapshot_nested_dispose() {
824 let _guard = reset_runtime();
825 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
826 let nested = parent.take_nested_snapshot(None);
827
828 assert_eq!(parent.nested_count.get(), 1);
829
830 nested.dispose();
831 assert_eq!(parent.nested_count.get(), 0);
832 }
833
834 #[test]
835 #[should_panic(expected = "Snapshot has already been applied")]
836 fn test_mutable_snapshot_write_after_apply_panics() {
837 let _guard = reset_runtime();
838 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
839 snapshot.apply().check();
840
841 let mock_state = Arc::new(MockStateObject);
842 snapshot.record_write(mock_state);
843 }
844
845 #[test]
846 #[should_panic(expected = "Snapshot has been disposed")]
847 fn test_mutable_snapshot_write_after_dispose_panics() {
848 let _guard = reset_runtime();
849 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
850 snapshot.dispose();
851
852 let mock_state = Arc::new(MockStateObject);
853 snapshot.record_write(mock_state);
854 }
855
856 #[test]
857 fn test_mutable_snapshot_dispose() {
858 let _guard = reset_runtime();
859 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
860 assert!(!snapshot.is_disposed());
861
862 snapshot.dispose();
863 assert!(snapshot.is_disposed());
864 }
865
866 #[test]
867 fn test_mutable_snapshot_apply_observer() {
868 let _guard = reset_runtime();
869 use std::sync::{Arc as StdArc, Mutex};
870
871 let applied_count = StdArc::new(Mutex::new(0));
872 let applied_count_clone = applied_count.clone();
873
874 let observer = Rc::new(
875 move |_modified: &[Arc<dyn StateObject>], _snapshot_id: SnapshotId| {
876 *applied_count_clone.lock().unwrap() += 1;
877 },
878 );
879
880 let _handle = register_apply_observer(observer);
881
882 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
883 let state = new_state(0);
884
885 snapshot.enter(|| state.set(10));
886 snapshot.apply().check();
887
888 assert_eq!(*applied_count.lock().unwrap(), 1);
889 }
890
891 #[test]
892 fn test_mutable_conflict_detection_same_object() {
893 let _guard = reset_runtime();
894 let global = GlobalSnapshot::get_or_create();
895 let state = new_state(0);
896
897 let s1 = global.take_nested_mutable_snapshot(None, None);
898 s1.enter(|| state.set(1));
899
900 let s2 = global.take_nested_mutable_snapshot(None, None);
901 s2.enter(|| state.set(2));
902
903 assert!(s1.apply().is_success());
904 assert!(s2.apply().is_failure());
905 }
906
907 #[test]
908 fn test_mutable_no_conflict_different_objects() {
909 let _guard = reset_runtime();
910 let global = GlobalSnapshot::get_or_create();
911 let state1 = new_state(0);
912 let state2 = new_state(0);
913
914 let s1 = global.take_nested_mutable_snapshot(None, None);
915 s1.enter(|| state1.set(10));
916
917 let s2 = global.take_nested_mutable_snapshot(None, None);
918 s2.enter(|| state2.set(20));
919
920 assert!(s1.apply().is_success());
921 assert!(s2.apply().is_success());
922 }
923}