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(
124 id: SnapshotId,
125 invalid: SnapshotIdSet,
126 read_observer: Option<ReadObserver>,
127 write_observer: Option<WriteObserver>,
128 base_parent_id: SnapshotId,
129 ) -> Arc<Self> {
130 Self::from_parts(
131 id,
132 invalid,
133 read_observer,
134 write_observer,
135 base_parent_id,
136 false,
137 )
138 }
139
140 fn validate_not_applied(&self) {
141 if self.applied.get() {
142 panic!("Snapshot has already been applied");
143 }
144 }
145
146 fn validate_not_disposed(&self) {
147 if self.state.disposed.get() {
148 panic!("Snapshot has been disposed");
149 }
150 }
151
152 pub fn snapshot_id(&self) -> SnapshotId {
153 self.state.id.get()
154 }
155
156 pub fn invalid(&self) -> SnapshotIdSet {
157 self.state.invalid.borrow().clone()
158 }
159
160 pub fn read_only(&self) -> bool {
161 false
162 }
163
164 pub(crate) fn set_on_dispose<F>(&self, f: F)
165 where
166 F: FnOnce() + 'static,
167 {
168 self.state.set_on_dispose(f);
169 }
170
171 pub fn root_mutable(self: &Arc<Self>) -> Arc<Self> {
172 self.clone()
173 }
174
175 pub fn enter<T>(self: &Arc<Self>, f: impl FnOnce() -> T) -> T {
176 enter_snapshot_scope(AnySnapshot::Mutable(self.clone()), f)
177 }
178
179 pub fn take_nested_snapshot(
180 self: &Arc<Self>,
181 read_observer: Option<ReadObserver>,
182 ) -> Arc<ReadonlySnapshot> {
183 self.validate_not_disposed();
184 self.validate_not_applied();
185
186 let merged_observer =
187 merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
188
189 let nested = ReadonlySnapshot::new(
191 self.state.id.get(),
192 self.state.invalid.borrow().clone(),
193 merged_observer,
194 );
195
196 self.nested_count.set(self.nested_count.get() + 1);
197
198 let parent_weak = Arc::downgrade(self);
200 nested.set_on_dispose(move || {
201 if let Some(parent) = parent_weak.upgrade() {
202 let cur = parent.nested_count.get();
203 if cur > 0 {
204 parent.nested_count.set(cur - 1);
205 }
206 }
207 });
208 nested
209 }
210
211 pub fn has_pending_changes(&self) -> bool {
212 !self.state.modified.borrow().is_empty()
213 }
214
215 pub fn pending_children(&self) -> Vec<SnapshotId> {
216 self.state.pending_children()
217 }
218
219 pub fn has_pending_children(&self) -> bool {
220 self.state.has_pending_children()
221 }
222
223 pub fn dispose(&self) {
224 if !self.state.disposed.get() && self.nested_count.get() == 0 {
225 self.state.dispose();
226 }
227 }
228
229 pub fn record_read(&self, state: &dyn StateObject) {
230 self.state.record_read(state);
231 }
232
233 pub fn record_write(&self, state: Arc<dyn StateObject>) {
234 self.validate_not_applied();
235 self.validate_not_disposed();
236 self.state.record_write(state, self.state.id.get());
237 }
238
239 pub fn close(&self) {
240 self.state.disposed.set(true);
241 }
242
243 pub fn is_disposed(&self) -> bool {
244 self.state.disposed.get()
245 }
246
247 pub fn apply(&self) -> SnapshotApplyResult {
248 if self.state.disposed.get() {
250 return SnapshotApplyResult::Failure;
251 }
252
253 if self.applied.get() {
254 return SnapshotApplyResult::Failure;
255 }
256
257 let modified = self.state.modified.borrow();
258 if modified.is_empty() {
259 self.applied.set(true);
261 self.state.dispose();
262 return SnapshotApplyResult::Success;
263 }
264
265 let this_id = self.state.id.get();
266 let mut modified_objects: Vec<(StateObjectId, Arc<dyn StateObject>, SnapshotId)> =
267 Vec::with_capacity(modified.len());
268 for (&obj_id, (obj, writer_id)) in modified.iter() {
269 modified_objects.push((obj_id, obj.clone(), *writer_id));
270 }
271
272 drop(modified);
273
274 let parent_snapshot = GlobalSnapshot::get_or_create();
275 let parent_snapshot_id = parent_snapshot.snapshot_id();
276 let parent_invalid = parent_snapshot.invalid();
277 drop(parent_snapshot);
278
279 let next_invalid = super::runtime::open_snapshots().clear(parent_snapshot_id);
280 let this_invalid_for_optimistic = self.state.invalid.borrow().clone();
281 let optimistic = super::optimistic_merges(
282 parent_snapshot_id,
283 self.base_parent_id,
284 &modified_objects,
285 &next_invalid,
286 &this_invalid_for_optimistic,
287 );
288
289 let mut operations: Vec<ApplyOperation> = Vec::with_capacity(modified_objects.len());
290
291 for (obj_id, state, writer_id) in &modified_objects {
292 let head = state.first_record();
293 let applied = match find_record_by_id(&head, *writer_id) {
294 Some(record) => record,
295 None => return SnapshotApplyResult::Failure,
296 };
297
298 let Some(current) =
299 crate::state::readable_record_for(&head, parent_snapshot_id, &next_invalid)
300 .or_else(|| state.try_readable_record(parent_snapshot_id, &parent_invalid))
301 else {
302 log::error!(
303 "MutableSnapshot::apply missing parent readable record (object_id={:?}, parent_snapshot_id={})",
304 obj_id,
305 parent_snapshot_id
306 );
307 return SnapshotApplyResult::Failure;
308 };
309 let this_invalid = self.state.invalid.borrow();
312 let (previous_opt, found_base) =
313 find_previous_record(&head, self.base_parent_id, &this_invalid);
314 drop(this_invalid);
315 let Some(previous) = previous_opt else {
316 return SnapshotApplyResult::Failure;
317 };
318
319 if !found_base || previous.snapshot_id() == PREEXISTING_SNAPSHOT_ID {
320 operations.push(ApplyOperation::PromoteChild {
321 object_id: *obj_id,
322 state: state.clone(),
323 writer_id: *writer_id,
324 });
325 continue;
326 }
327
328 if Rc::ptr_eq(¤t, &previous) {
329 operations.push(ApplyOperation::PromoteChild {
330 object_id: *obj_id,
331 state: state.clone(),
332 writer_id: *writer_id,
333 });
334 continue;
335 }
336
337 let merged = if let Some(candidate) = optimistic
338 .as_ref()
339 .and_then(|map| map.get(&(Rc::as_ptr(¤t) as usize)))
340 .cloned()
341 {
342 candidate
343 } else {
344 match state.merge_records(
345 Rc::clone(&previous),
346 Rc::clone(¤t),
347 Rc::clone(&applied),
348 ) {
349 Some(record) => record,
350 None => return SnapshotApplyResult::Failure,
351 }
352 };
353
354 if Rc::ptr_eq(&merged, &applied) {
355 operations.push(ApplyOperation::PromoteChild {
356 object_id: *obj_id,
357 state: state.clone(),
358 writer_id: *writer_id,
359 });
360 } else if Rc::ptr_eq(&merged, ¤t) {
361 operations.push(ApplyOperation::PromoteExisting {
362 object_id: *obj_id,
363 state: state.clone(),
364 source_id: current.snapshot_id(),
365 applied: applied.clone(),
366 });
367 } else {
368 operations.push(ApplyOperation::CommitMerged {
369 object_id: *obj_id,
370 state: state.clone(),
371 merged: merged.clone(),
372 applied: applied.clone(),
373 });
374 }
375 }
376
377 let mut applied_info: Vec<(StateObjectId, Arc<dyn StateObject>, SnapshotId)> =
378 Vec::with_capacity(operations.len());
379
380 for operation in operations {
381 match operation {
382 ApplyOperation::PromoteChild {
383 object_id,
384 state,
385 writer_id,
386 } => {
387 if state.promote_record(writer_id).is_err() {
388 return SnapshotApplyResult::Failure;
389 }
390 let new_head_id = state.first_record().snapshot_id();
391 applied_info.push((object_id, state, new_head_id));
392 }
393 ApplyOperation::PromoteExisting {
394 object_id,
395 state,
396 source_id,
397 applied,
398 } => {
399 if state.promote_record(source_id).is_err() {
400 return SnapshotApplyResult::Failure;
401 }
402 applied.set_tombstone(true);
403 applied.clear_value();
404 let new_head_id = state.first_record().snapshot_id();
405 applied_info.push((object_id, state, new_head_id));
406 }
407 ApplyOperation::CommitMerged {
408 object_id,
409 state,
410 merged,
411 applied,
412 } => {
413 let Ok(new_head_id) = state.commit_merged_record(merged) else {
414 return SnapshotApplyResult::Failure;
415 };
416 applied.set_tombstone(true);
417 applied.clear_value();
418 applied_info.push((object_id, state, new_head_id));
419 }
420 }
421 }
422
423 for (obj_id, _, head_id) in &applied_info {
424 super::set_last_write(*obj_id, *head_id);
425 }
426
427 self.applied.set(true);
428 self.state.dispose();
429
430 for (_, state, _) in &applied_info {
435 super::EXTRA_STATE_OBJECTS.with(|cell| {
436 cell.borrow_mut().add_trait_object(state);
437 });
438 }
439
440 let observer_states: Vec<Arc<dyn StateObject>> = applied_info
441 .iter()
442 .map(|(_, state, _)| state.clone())
443 .collect();
444 super::notify_apply_observers(&observer_states, this_id);
445 SnapshotApplyResult::Success
446 }
447
448 pub fn take_nested_mutable_snapshot(
449 self: &Arc<Self>,
450 read_observer: Option<ReadObserver>,
451 write_observer: Option<WriteObserver>,
452 ) -> Arc<NestedMutableSnapshot> {
453 self.validate_not_disposed();
454 self.validate_not_applied();
455
456 let merged_read =
457 merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
458 let merged_write =
459 merge_write_observers(write_observer, self.state.write_observer.borrow().clone());
460
461 let parent_id = self.state.id.get();
463 let current_invalid = self.state.invalid.borrow().clone();
464
465 let (new_id, _runtime_invalid) = allocate_snapshot();
467
468 let parent_invalid_with_child = current_invalid.set(new_id);
470 self.state.invalid.replace(parent_invalid_with_child);
471
472 let invalid = current_invalid.add_range(parent_id + 1, new_id);
476
477 let self_weak = Arc::downgrade(self);
478 let nested = NestedMutableSnapshot::new(
479 new_id,
480 invalid,
481 merged_read,
482 merged_write,
483 self_weak,
484 self.state.id.get(), );
486
487 self.nested_count.set(self.nested_count.get() + 1);
488 self.state.add_pending_child(new_id);
489
490 let parent_weak = Arc::downgrade(self);
491 nested.set_on_dispose({
492 let child_id = new_id;
493 move || {
494 if let Some(parent) = parent_weak.upgrade() {
495 if parent.nested_count.get() > 0 {
496 parent
497 .nested_count
498 .set(parent.nested_count.get().saturating_sub(1));
499 }
500 let mut invalid = parent.state.invalid.borrow_mut();
501 let new_set = invalid.clone().clear(child_id);
502 *invalid = new_set;
503 parent.state.remove_pending_child(child_id);
504 }
505 }
506 });
507
508 nested
509 }
510
511 pub(crate) fn merge_child_modifications(
516 &self,
517 child_modified: &HashMap<StateObjectId, (Arc<dyn StateObject>, SnapshotId)>,
518 ) -> Result<(), ()> {
519 {
521 let parent_mod = self.state.modified.borrow();
522 for key in child_modified.keys() {
523 if parent_mod.contains_key(key) {
524 return Err(());
525 }
526 }
527 }
528
529 let mut parent_mod = self.state.modified.borrow_mut();
531 for (key, value) in child_modified.iter() {
532 parent_mod.entry(*key).or_insert_with(|| value.clone());
533 }
534 Ok(())
535 }
536}
537
538#[cfg(test)]
539impl MutableSnapshot {
540 pub(crate) fn debug_modified_objects(
541 &self,
542 ) -> Vec<(StateObjectId, Arc<dyn StateObject>, SnapshotId)> {
543 let modified = self.state.modified.borrow();
544 modified
545 .iter()
546 .map(|(&obj_id, (state, writer_id))| (obj_id, state.clone(), *writer_id))
547 .collect()
548 }
549
550 pub(crate) fn debug_base_parent_id(&self) -> SnapshotId {
551 self.base_parent_id
552 }
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558 use crate::snapshot_v2::runtime::TestRuntimeGuard;
559 use crate::state::{NeverEqual, SnapshotMutableState, StateObject};
560 use std::sync::Arc;
561
562 fn reset_runtime() -> TestRuntimeGuard {
563 reset_runtime_for_tests()
564 }
565
566 fn new_state(initial: i32) -> Arc<SnapshotMutableState<i32>> {
567 SnapshotMutableState::new_in_arc(initial, Arc::new(NeverEqual))
568 }
569
570 struct MockStateObject;
572
573 fn mock_state_record() -> Rc<crate::state::StateRecord> {
574 crate::state::StateRecord::new(crate::state::PREEXISTING_SNAPSHOT_ID, (), None)
575 }
576
577 impl StateObject for MockStateObject {
578 fn object_id(&self) -> crate::state::ObjectId {
579 crate::state::ObjectId(0)
580 }
581
582 fn first_record(&self) -> Rc<crate::state::StateRecord> {
583 mock_state_record()
584 }
585
586 fn try_readable_record(
587 &self,
588 snapshot_id: crate::snapshot_id_set::SnapshotId,
589 invalid: &SnapshotIdSet,
590 ) -> Option<Rc<crate::state::StateRecord>> {
591 Some(self.readable_record(snapshot_id, invalid))
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 struct MissingParentReadableStateObject {
617 record: Rc<crate::state::StateRecord>,
618 }
619
620 impl MissingParentReadableStateObject {
621 fn new(writer_id: SnapshotId) -> Self {
622 Self {
623 record: crate::state::StateRecord::new(writer_id, (), None),
624 }
625 }
626 }
627
628 impl StateObject for MissingParentReadableStateObject {
629 fn object_id(&self) -> crate::state::ObjectId {
630 crate::state::ObjectId(10_001)
631 }
632
633 fn first_record(&self) -> Rc<crate::state::StateRecord> {
634 Rc::clone(&self.record)
635 }
636
637 fn try_readable_record(
638 &self,
639 _: SnapshotId,
640 _: &SnapshotIdSet,
641 ) -> Option<Rc<crate::state::StateRecord>> {
642 None
643 }
644
645 fn readable_record(
646 &self,
647 _: SnapshotId,
648 _: &SnapshotIdSet,
649 ) -> Rc<crate::state::StateRecord> {
650 panic!("apply must use a fallible parent readable-record lookup")
651 }
652
653 fn prepend_state_record(&self, _record: Rc<crate::state::StateRecord>) {}
654
655 fn promote_record(
656 &self,
657 _child_id: crate::snapshot_id_set::SnapshotId,
658 ) -> Result<(), &'static str> {
659 Ok(())
660 }
661
662 fn as_any(&self) -> &dyn std::any::Any {
663 self
664 }
665 }
666
667 #[test]
668 fn test_mutable_snapshot_creation() {
669 let _guard = reset_runtime();
670 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
671 assert_eq!(snapshot.snapshot_id(), 1);
672 assert!(!snapshot.read_only());
673 assert!(!snapshot.is_disposed());
674 assert!(!snapshot.applied.get());
675 }
676
677 #[test]
678 fn test_mutable_snapshot_no_pending_changes_initially() {
679 let _guard = reset_runtime();
680 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
681 assert!(!snapshot.has_pending_changes());
682 }
683
684 #[test]
685 fn test_mutable_snapshot_enter() {
686 let _guard = reset_runtime();
687 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
688
689 set_current_snapshot(None);
690 assert!(current_snapshot().is_none());
691
692 snapshot.enter(|| {
693 let current = current_snapshot();
694 assert!(current.is_some());
695 assert_eq!(current.unwrap().snapshot_id(), 1);
696 });
697
698 assert!(current_snapshot().is_none());
699 }
700
701 #[test]
702 fn test_mutable_snapshot_read_observer() {
703 let _guard = reset_runtime();
704 use std::sync::{Arc as StdArc, Mutex};
705
706 let read_count = StdArc::new(Mutex::new(0));
707 let read_count_clone = read_count.clone();
708
709 let observer = Arc::new(move |_: &dyn StateObject| {
710 *read_count_clone.lock().unwrap() += 1;
711 });
712
713 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), Some(observer), None, 0);
714 let mock_state = MockStateObject;
715
716 snapshot.record_read(&mock_state);
717 snapshot.record_read(&mock_state);
718
719 assert_eq!(*read_count.lock().unwrap(), 2);
720 }
721
722 #[test]
723 fn test_mutable_snapshot_write_observer() {
724 let _guard = reset_runtime();
725 use std::sync::{Arc as StdArc, Mutex};
726
727 let write_count = StdArc::new(Mutex::new(0));
728 let write_count_clone = write_count.clone();
729
730 let observer = Arc::new(move |_: &dyn StateObject| {
731 *write_count_clone.lock().unwrap() += 1;
732 });
733
734 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, Some(observer), 0);
735 let mock_state = Arc::new(MockStateObject);
736
737 snapshot.record_write(mock_state.clone());
738 snapshot.record_write(mock_state.clone()); assert_eq!(*write_count.lock().unwrap(), 1);
741 }
742
743 #[test]
744 fn test_mutable_snapshot_apply_empty() {
745 let _guard = reset_runtime();
746 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
747 let result = snapshot.apply();
748 assert!(result.is_success());
749 assert!(snapshot.applied.get());
750 }
751
752 #[test]
753 fn mutable_apply_returns_failure_when_parent_readable_record_is_missing() {
754 let _guard = reset_runtime();
755 let snapshot = MutableSnapshot::new(7, SnapshotIdSet::new(), None, None, 1);
756 let state = Arc::new(MissingParentReadableStateObject::new(
757 snapshot.snapshot_id(),
758 ));
759
760 snapshot.record_write(state);
761
762 let result = snapshot.apply();
763
764 assert!(result.is_failure());
765 }
766
767 #[test]
768 fn test_mutable_snapshot_apply_twice_fails() {
769 let _guard = reset_runtime();
770 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
771 snapshot.apply().check();
772
773 let result = snapshot.apply();
774 assert!(result.is_failure());
775 }
776
777 #[test]
778 fn test_mutable_snapshot_nested_readonly() {
779 let _guard = reset_runtime();
780 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
781 let nested = parent.take_nested_snapshot(None);
782
783 assert_eq!(nested.snapshot_id(), 1);
784 assert!(nested.read_only());
785 assert_eq!(parent.nested_count.get(), 1);
786 }
787
788 #[test]
789 fn test_mutable_snapshot_nested_mutable() {
790 let _guard = reset_runtime();
791 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
792 let nested = parent.take_nested_mutable_snapshot(None, None);
793
794 assert!(nested.snapshot_id() > parent.snapshot_id());
795 assert!(!nested.read_only());
796 assert_eq!(parent.nested_count.get(), 1);
797 }
798
799 #[test]
800 fn test_mutable_snapshot_nested_mutable_dispose_clears_invalid() {
801 let _guard = reset_runtime();
802 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
803 let nested = parent.take_nested_mutable_snapshot(None, None);
804
805 let child_id = nested.snapshot_id();
806 assert!(parent.state.invalid.borrow().get(child_id));
807
808 nested.dispose();
809
810 assert_eq!(parent.nested_count.get(), 0);
811 assert!(!parent.state.invalid.borrow().get(child_id));
812 }
813
814 #[test]
815 fn test_mutable_snapshot_nested_dispose() {
816 let _guard = reset_runtime();
817 let parent = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
818 let nested = parent.take_nested_snapshot(None);
819
820 assert_eq!(parent.nested_count.get(), 1);
821
822 nested.dispose();
823 assert_eq!(parent.nested_count.get(), 0);
824 }
825
826 #[test]
827 #[should_panic(expected = "Snapshot has already been applied")]
828 fn test_mutable_snapshot_write_after_apply_panics() {
829 let _guard = reset_runtime();
830 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
831 snapshot.apply().check();
832
833 let mock_state = Arc::new(MockStateObject);
834 snapshot.record_write(mock_state);
835 }
836
837 #[test]
838 #[should_panic(expected = "Snapshot has been disposed")]
839 fn test_mutable_snapshot_write_after_dispose_panics() {
840 let _guard = reset_runtime();
841 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
842 snapshot.dispose();
843
844 let mock_state = Arc::new(MockStateObject);
845 snapshot.record_write(mock_state);
846 }
847
848 #[test]
849 fn test_mutable_snapshot_dispose() {
850 let _guard = reset_runtime();
851 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
852 assert!(!snapshot.is_disposed());
853
854 snapshot.dispose();
855 assert!(snapshot.is_disposed());
856 }
857
858 #[test]
859 fn test_mutable_snapshot_apply_observer() {
860 let _guard = reset_runtime();
861 use std::sync::{Arc as StdArc, Mutex};
862
863 let applied_count = StdArc::new(Mutex::new(0));
864 let applied_count_clone = applied_count.clone();
865
866 let observer = Rc::new(
867 move |_modified: &[Arc<dyn StateObject>], _snapshot_id: SnapshotId| {
868 *applied_count_clone.lock().unwrap() += 1;
869 },
870 );
871
872 let _handle = register_apply_observer(observer);
873
874 let snapshot = MutableSnapshot::new(1, SnapshotIdSet::new(), None, None, 0);
875 let state = new_state(0);
876
877 snapshot.enter(|| state.set(10));
878 snapshot.apply().check();
879
880 assert_eq!(*applied_count.lock().unwrap(), 1);
881 }
882
883 #[test]
884 fn test_mutable_conflict_detection_same_object() {
885 let _guard = reset_runtime();
886 let global = GlobalSnapshot::get_or_create();
887 let state = new_state(0);
888
889 let s1 = global.take_nested_mutable_snapshot(None, None);
890 s1.enter(|| state.set(1));
891
892 let s2 = global.take_nested_mutable_snapshot(None, None);
893 s2.enter(|| state.set(2));
894
895 assert!(s1.apply().is_success());
896 assert!(s2.apply().is_failure());
897 }
898
899 #[test]
900 fn test_mutable_no_conflict_different_objects() {
901 let _guard = reset_runtime();
902 let global = GlobalSnapshot::get_or_create();
903 let state1 = new_state(0);
904 let state2 = new_state(0);
905
906 let s1 = global.take_nested_mutable_snapshot(None, None);
907 s1.enter(|| state1.set(10));
908
909 let s2 = global.take_nested_mutable_snapshot(None, None);
910 s2.enter(|| state2.set(20));
911
912 assert!(s1.apply().is_success());
913 assert!(s2.apply().is_success());
914 }
915}