1use std::{
24 cell::{Cell, RefCell},
25 rc::{Rc, Weak},
26};
27
28use crate::{
29 collections::map::{HashMap, HashSet},
30 snapshot_id_set::{SnapshotId, SnapshotIdSet},
31 snapshot_pinning::{self, PinHandle},
32 snapshot_weak_set::SnapshotWeakSetDebugStats,
33 state::{StateObject, StateRecord},
34};
35
36mod global;
37mod mutable;
38mod nested;
39mod readonly;
40mod runtime;
41mod transparent;
42
43#[cfg(test)]
44#[path = "tests/integration_tests.rs"]
45mod integration_tests;
46
47pub use global::{GlobalSnapshot, advance_global_snapshot};
48pub use mutable::MutableSnapshot;
49pub use nested::{NestedMutableSnapshot, NestedReadonlySnapshot};
50pub use readonly::ReadonlySnapshot;
51pub(crate) use runtime::{SnapshotRuntime, allocate_snapshot, close_snapshot, with_runtime};
52#[cfg(test)]
53pub(crate) use runtime::{TestRuntimeGuard, reset_runtime_for_tests};
54pub use transparent::{TransparentObserverMutableSnapshot, TransparentObserverSnapshot};
55
56pub type ReadObserver = Rc<dyn Fn(&dyn StateObject) + 'static>;
58
59pub type WriteObserver = Rc<dyn Fn(&dyn StateObject) + 'static>;
61
62pub type ApplyObserver = Rc<dyn Fn(&[Rc<dyn StateObject>], SnapshotId) + 'static>;
64
65#[derive(Debug, Clone, PartialEq, Eq)]
67pub enum SnapshotApplyResult {
68 Success,
70 Failure,
72}
73
74impl SnapshotApplyResult {
75 pub fn is_success(&self) -> bool {
77 matches!(self, SnapshotApplyResult::Success)
78 }
79
80 pub fn is_failure(&self) -> bool {
82 matches!(self, SnapshotApplyResult::Failure)
83 }
84
85 #[track_caller]
87 pub fn check(&self) {
88 assert!(!self.is_failure(), "Snapshot apply failed");
89 }
90}
91
92pub type StateObjectId = usize;
94
95#[derive(Clone)]
100pub enum AnySnapshot {
101 Readonly(Rc<ReadonlySnapshot>),
103 Mutable(Rc<MutableSnapshot>),
105 NestedReadonly(Rc<NestedReadonlySnapshot>),
107 NestedMutable(Rc<NestedMutableSnapshot>),
109 Global(Rc<GlobalSnapshot>),
111 TransparentMutable(Rc<TransparentObserverMutableSnapshot>),
113 TransparentReadonly(Rc<TransparentObserverSnapshot>),
115}
116
117#[derive(Clone)]
124pub enum AnyMutableSnapshot {
125 Root(Rc<MutableSnapshot>),
127 Nested(Rc<NestedMutableSnapshot>),
129}
130
131impl AnyMutableSnapshot {
132 pub fn snapshot_id(&self) -> SnapshotId {
134 match self {
135 AnyMutableSnapshot::Root(s) => s.snapshot_id(),
136 AnyMutableSnapshot::Nested(s) => s.snapshot_id(),
137 }
138 }
139
140 pub fn invalid(&self) -> SnapshotIdSet {
142 match self {
143 AnyMutableSnapshot::Root(s) => s.invalid(),
144 AnyMutableSnapshot::Nested(s) => s.invalid(),
145 }
146 }
147
148 pub fn enter<T>(&self, f: impl FnOnce() -> T) -> T {
150 match self {
151 AnyMutableSnapshot::Root(s) => s.enter(f),
152 AnyMutableSnapshot::Nested(s) => s.enter(f),
153 }
154 }
155
156 pub fn apply(&self) -> SnapshotApplyResult {
158 match self {
159 AnyMutableSnapshot::Root(s) => s.apply(),
160 AnyMutableSnapshot::Nested(s) => s.apply(),
161 }
162 }
163
164 pub fn dispose(&self) {
166 match self {
167 AnyMutableSnapshot::Root(s) => s.dispose(),
168 AnyMutableSnapshot::Nested(s) => s.dispose(),
169 }
170 }
171}
172
173impl AnySnapshot {
174 pub fn snapshot_id(&self) -> SnapshotId {
176 match self {
177 AnySnapshot::Readonly(s) => s.snapshot_id(),
178 AnySnapshot::Mutable(s) => s.snapshot_id(),
179 AnySnapshot::NestedReadonly(s) => s.snapshot_id(),
180 AnySnapshot::NestedMutable(s) => s.snapshot_id(),
181 AnySnapshot::Global(s) => s.snapshot_id(),
182 AnySnapshot::TransparentMutable(s) => s.snapshot_id(),
183 AnySnapshot::TransparentReadonly(s) => s.snapshot_id(),
184 }
185 }
186
187 fn state(&self) -> &SnapshotState {
189 match self {
190 AnySnapshot::Readonly(s) => &s.state,
191 AnySnapshot::Mutable(s) => &s.state,
192 AnySnapshot::NestedReadonly(s) => &s.state,
193 AnySnapshot::NestedMutable(s) => &s.state,
194 AnySnapshot::Global(s) => &s.state,
195 AnySnapshot::TransparentMutable(s) => &s.state,
196 AnySnapshot::TransparentReadonly(s) => &s.state,
197 }
198 }
199
200 pub(crate) fn with_invalid<R>(&self, f: impl FnOnce(&SnapshotIdSet) -> R) -> R {
201 f(&self.state().invalid.borrow())
202 }
203
204 pub fn invalid(&self) -> SnapshotIdSet {
206 match self {
207 AnySnapshot::Readonly(s) => s.invalid(),
208 AnySnapshot::Mutable(s) => s.invalid(),
209 AnySnapshot::NestedReadonly(s) => s.invalid(),
210 AnySnapshot::NestedMutable(s) => s.invalid(),
211 AnySnapshot::Global(s) => s.invalid(),
212 AnySnapshot::TransparentMutable(s) => s.invalid(),
213 AnySnapshot::TransparentReadonly(s) => s.invalid(),
214 }
215 }
216
217 pub fn is_valid(&self, id: SnapshotId) -> bool {
219 let snapshot_id = self.snapshot_id();
220 id <= snapshot_id && !self.invalid().get(id)
221 }
222
223 pub fn read_only(&self) -> bool {
225 match self {
226 AnySnapshot::Readonly(_) => true,
227 AnySnapshot::Mutable(_) => false,
228 AnySnapshot::NestedReadonly(_) => true,
229 AnySnapshot::NestedMutable(_) => false,
230 AnySnapshot::Global(_) => false,
231 AnySnapshot::TransparentMutable(_) => false,
232 AnySnapshot::TransparentReadonly(_) => true,
233 }
234 }
235
236 pub fn root(&self) -> AnySnapshot {
238 match self {
239 AnySnapshot::Readonly(s) => AnySnapshot::Readonly(s.root_readonly()),
240 AnySnapshot::Mutable(s) => AnySnapshot::Mutable(s.root_mutable()),
241 AnySnapshot::NestedReadonly(s) => AnySnapshot::NestedReadonly(s.root_nested_readonly()),
242 AnySnapshot::NestedMutable(s) => AnySnapshot::Mutable(s.root_mutable()),
243 AnySnapshot::Global(_) => AnySnapshot::Global(GlobalSnapshot::get_or_create()),
244 AnySnapshot::TransparentMutable(s) => {
245 AnySnapshot::TransparentMutable(s.root_transparent_mutable())
246 }
247 AnySnapshot::TransparentReadonly(s) => {
248 AnySnapshot::TransparentReadonly(s.root_transparent_readonly())
249 }
250 }
251 }
252
253 pub fn is_same_transparent(&self, other: &Rc<TransparentObserverMutableSnapshot>) -> bool {
255 matches!(self, AnySnapshot::TransparentMutable(snapshot) if Rc::ptr_eq(snapshot, other))
256 }
257
258 pub fn is_same_transparent_mutable(
260 &self,
261 other: &Rc<TransparentObserverMutableSnapshot>,
262 ) -> bool {
263 self.is_same_transparent(other)
264 }
265
266 pub fn is_same_transparent_readonly(&self, other: &Rc<TransparentObserverSnapshot>) -> bool {
268 matches!(self, AnySnapshot::TransparentReadonly(snapshot) if Rc::ptr_eq(snapshot, other))
269 }
270
271 pub fn enter<T>(&self, f: impl FnOnce() -> T) -> T {
273 match self {
274 AnySnapshot::Readonly(s) => s.enter(f),
275 AnySnapshot::Mutable(s) => s.enter(f),
276 AnySnapshot::NestedReadonly(s) => s.enter(f),
277 AnySnapshot::NestedMutable(s) => s.enter(f),
278 AnySnapshot::Global(s) => s.enter(f),
279 AnySnapshot::TransparentMutable(s) => s.enter(f),
280 AnySnapshot::TransparentReadonly(s) => s.enter(f),
281 }
282 }
283
284 pub fn take_nested_snapshot(&self, read_observer: Option<ReadObserver>) -> AnySnapshot {
286 match self {
287 AnySnapshot::Readonly(s) => {
288 AnySnapshot::Readonly(s.take_nested_snapshot(read_observer))
289 }
290 AnySnapshot::Mutable(s) => AnySnapshot::Readonly(s.take_nested_snapshot(read_observer)),
291 AnySnapshot::NestedReadonly(s) => {
292 AnySnapshot::NestedReadonly(s.take_nested_snapshot(read_observer))
293 }
294 AnySnapshot::NestedMutable(s) => {
295 AnySnapshot::Readonly(s.take_nested_snapshot(read_observer))
296 }
297 AnySnapshot::Global(s) => AnySnapshot::Readonly(s.take_nested_snapshot(read_observer)),
298 AnySnapshot::TransparentMutable(s) => {
299 AnySnapshot::Readonly(s.take_nested_snapshot(read_observer))
300 }
301 AnySnapshot::TransparentReadonly(s) => {
302 AnySnapshot::TransparentReadonly(s.take_nested_snapshot(read_observer))
303 }
304 }
305 }
306
307 pub fn has_pending_changes(&self) -> bool {
309 match self {
310 AnySnapshot::Readonly(s) => s.has_pending_changes(),
311 AnySnapshot::Mutable(s) => s.has_pending_changes(),
312 AnySnapshot::NestedReadonly(s) => s.has_pending_changes(),
313 AnySnapshot::NestedMutable(s) => s.has_pending_changes(),
314 AnySnapshot::Global(s) => s.has_pending_changes(),
315 AnySnapshot::TransparentMutable(s) => s.has_pending_changes(),
316 AnySnapshot::TransparentReadonly(s) => s.has_pending_changes(),
317 }
318 }
319
320 pub fn dispose(&self) {
322 match self {
323 AnySnapshot::Readonly(s) => s.dispose(),
324 AnySnapshot::Mutable(s) => s.dispose(),
325 AnySnapshot::NestedReadonly(s) => s.dispose(),
326 AnySnapshot::NestedMutable(s) => s.dispose(),
327 AnySnapshot::Global(s) => s.dispose(),
328 AnySnapshot::TransparentMutable(s) => s.dispose(),
329 AnySnapshot::TransparentReadonly(s) => s.dispose(),
330 }
331 }
332
333 pub fn is_disposed(&self) -> bool {
335 match self {
336 AnySnapshot::Readonly(s) => s.is_disposed(),
337 AnySnapshot::Mutable(s) => s.is_disposed(),
338 AnySnapshot::NestedReadonly(s) => s.is_disposed(),
339 AnySnapshot::NestedMutable(s) => s.is_disposed(),
340 AnySnapshot::Global(s) => s.is_disposed(),
341 AnySnapshot::TransparentMutable(s) => s.is_disposed(),
342 AnySnapshot::TransparentReadonly(s) => s.is_disposed(),
343 }
344 }
345
346 pub fn record_read(&self, state: &dyn StateObject) {
348 match self {
349 AnySnapshot::Readonly(s) => s.record_read(state),
350 AnySnapshot::Mutable(s) => s.record_read(state),
351 AnySnapshot::NestedReadonly(s) => s.record_read(state),
352 AnySnapshot::NestedMutable(s) => s.record_read(state),
353 AnySnapshot::Global(s) => s.record_read(state),
354 AnySnapshot::TransparentMutable(s) => s.record_read(state),
355 AnySnapshot::TransparentReadonly(s) => s.record_read(state),
356 }
357 }
358
359 pub fn record_write(&self, state: Rc<dyn StateObject>) {
361 match self {
362 AnySnapshot::Readonly(s) => s.record_write(state),
363 AnySnapshot::Mutable(s) => s.record_write(state),
364 AnySnapshot::NestedReadonly(s) => s.record_write(state),
365 AnySnapshot::NestedMutable(s) => s.record_write(state),
366 AnySnapshot::Global(s) => s.record_write(state),
367 AnySnapshot::TransparentMutable(s) => s.record_write(state),
368 AnySnapshot::TransparentReadonly(s) => s.record_write(state),
369 }
370 }
371
372 pub fn apply(&self) -> SnapshotApplyResult {
374 match self {
375 AnySnapshot::Mutable(s) => s.apply(),
376 AnySnapshot::NestedMutable(s) => s.apply(),
377 AnySnapshot::Global(s) => s.apply(),
378 AnySnapshot::TransparentMutable(s) => s.apply(),
379 _ => panic!("Cannot apply a read-only snapshot"),
380 }
381 }
382
383 pub fn take_nested_mutable_snapshot(
385 &self,
386 read_observer: Option<ReadObserver>,
387 write_observer: Option<WriteObserver>,
388 ) -> AnySnapshot {
389 match self {
390 AnySnapshot::Mutable(s) => AnySnapshot::NestedMutable(
391 s.take_nested_mutable_snapshot(read_observer, write_observer),
392 ),
393 AnySnapshot::NestedMutable(s) => AnySnapshot::NestedMutable(
394 s.take_nested_mutable_snapshot(read_observer, write_observer),
395 ),
396 AnySnapshot::Global(s) => {
397 AnySnapshot::Mutable(s.take_nested_mutable_snapshot(read_observer, write_observer))
398 }
399 AnySnapshot::TransparentMutable(s) => AnySnapshot::TransparentMutable(
400 s.take_nested_mutable_snapshot(read_observer, write_observer),
401 ),
402 _ => panic!("Cannot take nested mutable snapshot from read-only snapshot"),
403 }
404 }
405}
406
407pub fn current_snapshot() -> Option<AnySnapshot> {
409 crate::thread_state::try_snapshots(|state| state.current.borrow().clone()).flatten()
410}
411
412pub(crate) fn current_snapshot_reads_into(observer: &ReadObserver) -> bool {
413 crate::thread_state::try_snapshots(|state| match &*state.current.borrow() {
414 Some(AnySnapshot::TransparentMutable(snapshot)) => {
415 snapshot.can_reuse() && snapshot.reads_into(observer)
416 }
417 _ => false,
418 })
419 .unwrap_or(false)
420}
421
422pub(crate) fn set_current_snapshot(snapshot: Option<AnySnapshot>) {
423 crate::thread_state::try_snapshots(|state| {
424 *state.current.borrow_mut() = snapshot;
425 });
426}
427
428struct CurrentSnapshotGuard {
429 previous: Option<AnySnapshot>,
430}
431
432impl CurrentSnapshotGuard {
433 fn enter(snapshot: AnySnapshot) -> Self {
434 let previous =
435 crate::thread_state::try_snapshots(|state| state.current.replace(Some(snapshot)))
436 .flatten();
437 Self { previous }
438 }
439}
440
441impl Drop for CurrentSnapshotGuard {
442 fn drop(&mut self) {
443 set_current_snapshot(self.previous.take());
444 }
445}
446
447pub(crate) fn enter_snapshot_scope<T>(snapshot: AnySnapshot, f: impl FnOnce() -> T) -> T {
448 let _guard = CurrentSnapshotGuard::enter(snapshot);
449 f()
450}
451
452pub fn take_mutable_snapshot(
461 read_observer: Option<ReadObserver>,
462 write_observer: Option<WriteObserver>,
463) -> AnyMutableSnapshot {
464 match current_snapshot() {
465 Some(AnySnapshot::Mutable(parent)) => AnyMutableSnapshot::Nested(
466 parent.take_nested_mutable_snapshot(read_observer, write_observer),
467 ),
468 Some(AnySnapshot::NestedMutable(parent)) => AnyMutableSnapshot::Nested(
469 parent.take_nested_mutable_snapshot(read_observer, write_observer),
470 ),
471 _ => AnyMutableSnapshot::Root(
472 GlobalSnapshot::get_or_create()
473 .take_nested_mutable_snapshot(read_observer, write_observer),
474 ),
475 }
476}
477
478pub fn take_transparent_observer_mutable_snapshot(
487 read_observer: Option<ReadObserver>,
488 write_observer: Option<WriteObserver>,
489) -> Rc<TransparentObserverMutableSnapshot> {
490 take_transparent_observer_mutable_snapshot_reusing(read_observer, write_observer, None)
491}
492
493pub(crate) fn take_transparent_observer_mutable_snapshot_reusing(
494 read_observer: Option<ReadObserver>,
495 write_observer: Option<WriteObserver>,
496 recycled: Option<Rc<TransparentObserverMutableSnapshot>>,
497) -> Rc<TransparentObserverMutableSnapshot> {
498 let parent = current_snapshot();
499 match parent {
500 Some(AnySnapshot::TransparentMutable(transparent)) if transparent.can_reuse() => {
501 let (parent_read, parent_write) = transparent.observers();
502 if already_observes(&read_observer, &parent_read)
503 && already_observes(&write_observer, &parent_write)
504 {
505 return transparent;
506 }
507 TransparentObserverMutableSnapshot::new_reusing(
508 recycled,
509 transparent.snapshot_id(),
510 transparent.invalid(),
511 merge_read_observers(read_observer, parent_read),
512 merge_write_observers(write_observer, parent_write),
513 Some(Rc::downgrade(&transparent)),
514 )
515 }
516 _ => {
517 let current = current_snapshot()
518 .unwrap_or_else(|| AnySnapshot::Global(GlobalSnapshot::get_or_create()));
519 let id = current.snapshot_id();
520 let invalid = current.invalid();
521 TransparentObserverMutableSnapshot::new_reusing(
522 recycled,
523 id,
524 invalid,
525 read_observer,
526 write_observer,
527 None,
528 )
529 }
530 }
531}
532
533fn already_observes(requested: &Option<ReadObserver>, installed: &Option<ReadObserver>) -> bool {
534 match (requested, installed) {
535 (None, _) => true,
536 (Some(requested), Some(installed)) => Rc::ptr_eq(requested, installed),
537 (Some(_), None) => false,
538 }
539}
540
541pub fn allocate_record_id() -> SnapshotId {
543 runtime::allocate_record_id()
544}
545
546pub(crate) fn peek_next_snapshot_id() -> SnapshotId {
547 runtime::peek_next_snapshot_id()
548}
549
550#[derive(Clone)]
551pub(crate) struct ObserverId(Rc<()>);
552
553impl ObserverId {
554 fn new() -> Self {
555 Self(Rc::new(()))
556 }
557}
558
559impl PartialEq for ObserverId {
560 fn eq(&self, other: &Self) -> bool {
561 Rc::ptr_eq(&self.0, &other.0)
562 }
563}
564
565impl Eq for ObserverId {}
566
567pub(crate) struct ApplyObservers {
570 entries: Vec<(ObserverId, ApplyObserver)>,
571 running: Option<Rc<[ApplyObserver]>>,
572}
573
574impl ApplyObservers {
575 pub(crate) const fn new() -> Self {
576 Self {
577 entries: Vec::new(),
578 running: None,
579 }
580 }
581
582 fn insert(&mut self, id: ObserverId, observer: ApplyObserver) {
583 self.entries.push((id, observer));
584 self.running = None;
585 }
586
587 fn remove(&mut self, id: &ObserverId) {
588 if let Some(at) = self.entries.iter().position(|(entry, _)| entry == id) {
589 self.entries.remove(at);
590 self.running = None;
591 }
592 }
593
594 fn running(&mut self) -> Rc<[ApplyObserver]> {
597 let entries = &self.entries;
598 Rc::clone(self.running.get_or_insert_with(|| {
599 entries
600 .iter()
601 .map(|(_, observer)| Rc::clone(observer))
602 .collect()
603 }))
604 }
605}
606
607const UNUSED_RECORD_CLEANUP_INTERVAL: SnapshotId = 2;
608const UNUSED_RECORD_CLEANUP_BUSY_INTERVAL: SnapshotId = 1;
609const UNUSED_RECORD_CLEANUP_MIN_SIZE: usize = 64;
610
611#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
615pub struct SnapshotV2DebugStats {
616 pub apply_observers_len: usize,
618 pub apply_observers_cap: usize,
620 pub last_writes_len: usize,
622 pub last_writes_cap: usize,
624 pub extra_state_objects_len: usize,
626 pub extra_state_objects_cap: usize,
628 pub last_unused_record_cleanup: SnapshotId,
630}
631
632pub fn debug_snapshot_v2_stats() -> SnapshotV2DebugStats {
634 crate::thread_state::snapshots(|state| {
635 let observers = state.apply_observers.borrow();
636 let writes = state.last_writes.borrow();
637 let SnapshotWeakSetDebugStats {
638 len: extra_state_objects_len,
639 capacity: extra_state_objects_cap,
640 } = state.extra_state_objects.borrow().debug_stats();
641 SnapshotV2DebugStats {
642 apply_observers_len: observers.entries.len(),
643 apply_observers_cap: observers.entries.capacity(),
644 last_writes_len: writes.len(),
645 last_writes_cap: writes.capacity(),
646 extra_state_objects_len,
647 extra_state_objects_cap,
648 last_unused_record_cleanup: state.last_unused_record_cleanup.get(),
649 }
650 })
651}
652
653pub fn register_apply_observer(observer: ApplyObserver) -> ObserverHandle {
657 crate::thread_state::watch_thread_exit();
658 let id = ObserverId::new();
659 crate::thread_state::snapshots(|state| {
660 state
661 .apply_observers
662 .borrow_mut()
663 .insert(id.clone(), observer);
664 });
665 ObserverHandle {
666 kind: ObserverKind::Apply,
667 id,
668 }
669}
670
671pub struct ObserverHandle {
675 kind: ObserverKind,
676 id: ObserverId,
677}
678
679enum ObserverKind {
680 Apply,
681}
682
683impl Drop for ObserverHandle {
684 fn drop(&mut self) {
685 match self.kind {
686 ObserverKind::Apply => {
687 crate::thread_state::try_snapshots(|state| {
691 state.apply_observers.borrow_mut().remove(&self.id);
692 });
693 }
694 }
695 }
696}
697
698pub(crate) fn notify_apply_observers(modified: &[Rc<dyn StateObject>], snapshot_id: SnapshotId) {
699 let observers =
700 crate::thread_state::snapshots(|state| state.apply_observers.borrow_mut().running());
701 for observer in observers.iter() {
702 observer(modified, snapshot_id);
703 }
704}
705
706pub(crate) fn set_last_write(id: StateObjectId, snapshot_id: SnapshotId) {
707 crate::thread_state::snapshots(|state| {
708 state.last_writes.borrow_mut().insert(id, snapshot_id);
709 });
710}
711
712#[cfg(test)]
713pub(crate) fn clear_last_writes() {
714 crate::thread_state::snapshots(|state| state.last_writes.borrow_mut().clear());
715}
716
717pub(crate) fn check_and_overwrite_unused_records_locked() {
718 let _retiring = super::state::RetiredValuesScope::enter();
719 crate::thread_state::snapshots(|state| {
720 state
721 .extra_state_objects
722 .borrow_mut()
723 .remove_if(super::state::StateObject::overwrite_unused_records);
724 });
725}
726
727pub(crate) fn maybe_check_and_overwrite_unused_records_locked(current_snapshot_id: SnapshotId) {
728 let should_run = crate::thread_state::snapshots(|state| {
729 let set = state.extra_state_objects.borrow();
730 if set.is_empty() {
731 return false;
732 }
733 let interval = if set.len() >= UNUSED_RECORD_CLEANUP_MIN_SIZE {
734 UNUSED_RECORD_CLEANUP_BUSY_INTERVAL
735 } else {
736 UNUSED_RECORD_CLEANUP_INTERVAL
737 };
738 let due =
739 current_snapshot_id.saturating_sub(state.last_unused_record_cleanup.get()) >= interval;
740 if due {
741 state.last_unused_record_cleanup.set(current_snapshot_id);
742 }
743 due
744 });
745
746 if should_run {
747 check_and_overwrite_unused_records_locked();
748 }
749}
750
751#[cfg(test)]
752pub(crate) fn clear_unused_record_cleanup_for_tests() {
753 crate::thread_state::snapshots(|state| state.last_unused_record_cleanup.set(0));
754}
755
756pub(crate) fn optimistic_merges(
757 current_snapshot_id: SnapshotId,
758 base_parent_id: SnapshotId,
759 modified_objects: &[(StateObjectId, Rc<dyn StateObject>, SnapshotId)],
760 invalid_snapshots: &SnapshotIdSet,
761 applying_invalid: &SnapshotIdSet,
762) -> Option<HashMap<usize, Rc<StateRecord>>> {
763 if modified_objects.is_empty() {
764 return None;
765 }
766
767 let mut result: Option<HashMap<usize, Rc<StateRecord>>> = None;
768
769 for (_, state, writer_id) in modified_objects {
770 let head = state.first_record();
771
772 let Some(current) =
773 crate::state::readable_record_for(&head, current_snapshot_id, invalid_snapshots)
774 else {
775 continue;
776 };
777
778 let (previous_opt, found_base) =
779 mutable::find_previous_record(&head, base_parent_id, applying_invalid);
780 let previous = previous_opt?;
781
782 if !found_base || previous.snapshot_id() == crate::state::PREEXISTING_SNAPSHOT_ID {
783 continue;
784 }
785
786 if Rc::ptr_eq(¤t, &previous) {
787 continue;
788 }
789
790 let applied = mutable::find_record_by_id(&head, *writer_id)?;
791
792 let merged = state.merge_records(
793 Rc::clone(&previous),
794 Rc::clone(¤t),
795 Rc::clone(&applied),
796 )?;
797
798 result
799 .get_or_insert_with(HashMap::default)
800 .insert(Rc::as_ptr(¤t) as usize, merged);
801 }
802
803 result
804}
805
806fn merge_observers(a: Option<ReadObserver>, b: Option<ReadObserver>) -> Option<ReadObserver> {
807 match (a, b) {
808 (None, None) => None,
809 (Some(a), None) => Some(a),
810 (None, Some(b)) => Some(b),
811 (Some(a), Some(b)) => Some(Rc::new(move |state: &dyn StateObject| {
812 a(state);
813 b(state);
814 })),
815 }
816}
817
818pub fn merge_read_observers(
824 a: Option<ReadObserver>,
825 b: Option<ReadObserver>,
826) -> Option<ReadObserver> {
827 merge_observers(a, b)
828}
829
830pub fn merge_write_observers(
836 a: Option<WriteObserver>,
837 b: Option<WriteObserver>,
838) -> Option<WriteObserver> {
839 merge_observers(a, b)
840}
841
842pub(crate) struct SnapshotState {
843 pub(crate) id: Cell<SnapshotId>,
844 pub(crate) invalid: RefCell<SnapshotIdSet>,
845 pub(crate) pin_handle: Cell<PinHandle>,
846 pub(crate) disposed: Cell<bool>,
847 pub(crate) read_observer: RefCell<Option<ReadObserver>>,
848 pub(crate) write_observer: RefCell<Option<WriteObserver>>,
849 #[expect(clippy::type_complexity)]
850 pub(crate) modified: RefCell<HashMap<StateObjectId, (Rc<dyn StateObject>, SnapshotId)>>,
851 on_dispose: RefCell<Option<Box<dyn FnOnce()>>>,
852 runtime_tracked: bool,
853 pending_children: RefCell<HashSet<SnapshotId>>,
854}
855
856impl SnapshotState {
857 pub(crate) fn new(
858 id: SnapshotId,
859 invalid: SnapshotIdSet,
860 read_observer: Option<ReadObserver>,
861 write_observer: Option<WriteObserver>,
862 runtime_tracked: bool,
863 ) -> Self {
864 Self::new_with_pinning(
865 id,
866 invalid,
867 read_observer,
868 write_observer,
869 runtime_tracked,
870 true,
871 )
872 }
873
874 pub(crate) fn new_with_pinning(
875 id: SnapshotId,
876 invalid: SnapshotIdSet,
877 read_observer: Option<ReadObserver>,
878 write_observer: Option<WriteObserver>,
879 runtime_tracked: bool,
880 should_pin: bool,
881 ) -> Self {
882 let pin_handle = if should_pin {
883 snapshot_pinning::track_pinning(id, &invalid)
884 } else {
885 snapshot_pinning::PinHandle::INVALID
886 };
887 Self {
888 id: Cell::new(id),
889 invalid: RefCell::new(invalid),
890 pin_handle: Cell::new(pin_handle),
891 disposed: Cell::new(false),
892 read_observer: RefCell::new(read_observer),
893 write_observer: RefCell::new(write_observer),
894 modified: RefCell::new(HashMap::default()),
895 on_dispose: RefCell::new(None),
896 runtime_tracked,
897 pending_children: RefCell::new(HashSet::default()),
898 }
899 }
900
901 pub(crate) fn record_read(&self, state: &dyn StateObject) {
902 if let Some(observer) = self.read_observer.borrow().as_ref() {
903 observer(state);
904 }
905 }
906
907 pub(crate) fn record_write(&self, state: Rc<dyn StateObject>, writer_id: SnapshotId) {
908 let state_id = state.object_id().as_usize();
909
910 let mut modified = self.modified.borrow_mut();
911
912 match modified.entry(state_id) {
913 std::collections::hash_map::Entry::Vacant(e) => {
914 if let Some(observer) = self.write_observer.borrow().as_ref() {
915 observer(&*state);
916 }
917 e.insert((state, writer_id));
918 }
919 std::collections::hash_map::Entry::Occupied(mut e) => {
920 e.insert((state, writer_id));
921 }
922 }
923 }
924
925 pub(crate) fn dispose(&self) {
926 if !self.disposed.replace(true) {
927 let pin_handle = self.pin_handle.get();
928 snapshot_pinning::release_pinning(pin_handle);
929 if let Some(cb) = self.on_dispose.borrow_mut().take() {
930 cb();
931 }
932 if self.runtime_tracked {
933 close_snapshot(self.id.get());
934 }
935 }
936 }
937
938 pub(crate) fn add_pending_child(&self, id: SnapshotId) {
939 self.pending_children.borrow_mut().insert(id);
940 }
941
942 pub(crate) fn remove_pending_child(&self, id: SnapshotId) {
943 self.pending_children.borrow_mut().remove(&id);
944 }
945
946 pub(crate) fn has_pending_children(&self) -> bool {
947 !self.pending_children.borrow().is_empty()
948 }
949
950 pub(crate) fn pending_children(&self) -> Vec<SnapshotId> {
951 self.pending_children.borrow().iter().copied().collect()
952 }
953
954 pub(crate) fn set_on_dispose<F>(&self, f: F)
955 where
956 F: FnOnce() + 'static,
957 {
958 *self.on_dispose.borrow_mut() = Some(Box::new(f));
959 }
960}
961
962pub(crate) trait NestedMutableHost {
963 fn snapshot_state(&self) -> &SnapshotState;
964 fn nested_count(&self) -> &Cell<usize>;
965}
966
967pub(crate) fn clear_nested_child_on_dispose<P>(
968 parent: &Rc<P>,
969 child_id: SnapshotId,
970) -> impl FnOnce() + 'static
971where
972 P: NestedMutableHost + 'static,
973{
974 let weak = Rc::downgrade(parent);
975 move || {
976 if let Some(parent) = weak.upgrade() {
977 let nested_count = parent.nested_count();
978 if nested_count.get() > 0 {
979 nested_count.set(nested_count.get().saturating_sub(1));
980 }
981 let state = parent.snapshot_state();
982 let new_invalid = state.invalid.borrow().clone().clear(child_id);
983 state.invalid.replace(new_invalid);
984 state.remove_pending_child(child_id);
985 }
986 }
987}
988
989pub(crate) fn allocate_nested_mutable_snapshot<P>(
990 parent: &Rc<P>,
991 root: Weak<MutableSnapshot>,
992 read_observer: Option<ReadObserver>,
993 write_observer: Option<WriteObserver>,
994) -> Rc<NestedMutableSnapshot>
995where
996 P: NestedMutableHost + 'static,
997{
998 let state = parent.snapshot_state();
999 let merged_read = merge_read_observers(read_observer, state.read_observer.borrow().clone());
1000 let merged_write = merge_write_observers(write_observer, state.write_observer.borrow().clone());
1001
1002 let parent_id = state.id.get();
1003 let current_invalid = state.invalid.borrow().clone();
1004
1005 let (new_id, _runtime_invalid) = allocate_snapshot();
1006
1007 let parent_invalid_with_child = current_invalid.set(new_id);
1008 state.invalid.replace(parent_invalid_with_child);
1009
1010 let invalid = current_invalid.add_range(parent_id + 1, new_id);
1011
1012 let nested = NestedMutableSnapshot::new(
1013 new_id,
1014 invalid,
1015 merged_read,
1016 merged_write,
1017 root,
1018 state.id.get(),
1019 );
1020
1021 let nested_count = parent.nested_count();
1022 nested_count.set(nested_count.get() + 1);
1023 state.add_pending_child(new_id);
1024
1025 nested.set_on_dispose(clear_nested_child_on_dispose(parent, new_id));
1026
1027 nested
1028}
1029
1030#[cfg(test)]
1031#[path = "tests/snapshot_v2_tests.rs"]
1032mod tests;