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