cranpose_core/snapshot_v2/
transparent.rs

1//! Transparent observer snapshot implementations.
2//!
3//! These snapshots are optimized for observer chaining, allowing observers
4//! to be temporarily changed without allocating new snapshot structures.
5
6use super::*;
7
8/// A transparent mutable snapshot that allows observer replacement.
9///
10/// This snapshot type is optimized for cases where observers need to be
11/// temporarily added or removed without creating a new snapshot structure.
12///
13/// # Thread Safety
14/// Contains `Cell<T>` and `RefCell<T>` which are not `Send`/`Sync`. This is safe because
15/// snapshots are stored in thread-local storage and never shared across threads. The `Arc`
16/// is used for cheap cloning within a single thread, not for cross-thread sharing.
17#[allow(clippy::arc_with_non_send_sync)]
18pub struct TransparentObserverMutableSnapshot {
19    state: SnapshotState,
20    parent: Option<Weak<TransparentObserverMutableSnapshot>>,
21    nested_count: Cell<usize>,
22    applied: Cell<bool>,
23    /// Whether this snapshot can be reused for observer changes
24    reusable: Cell<bool>,
25}
26
27impl TransparentObserverMutableSnapshot {
28    pub fn new(
29        id: SnapshotId,
30        invalid: SnapshotIdSet,
31        read_observer: Option<ReadObserver>,
32        write_observer: Option<WriteObserver>,
33        parent: Option<Weak<TransparentObserverMutableSnapshot>>,
34    ) -> Arc<Self> {
35        Arc::new(Self {
36            state: SnapshotState::new(id, invalid, read_observer, write_observer, false),
37            parent,
38            nested_count: Cell::new(0),
39            applied: Cell::new(false),
40            reusable: Cell::new(true),
41        })
42    }
43
44    /// Check if this snapshot can be reused for observer changes.
45    pub fn can_reuse(&self) -> bool {
46        self.reusable.get()
47    }
48
49    /// Set the read observer (only allowed if reusable).
50    pub fn set_read_observer(&self, _observer: Option<ReadObserver>) {
51        if !self.can_reuse() {
52            panic!("Cannot change observers on non-reusable snapshot");
53        }
54        // In a full implementation, this would update the observer
55        // For now, this is a placeholder
56    }
57
58    /// Set the write observer (only allowed if reusable).
59    pub fn set_write_observer(&self, _observer: Option<WriteObserver>) {
60        if !self.can_reuse() {
61            panic!("Cannot change observers on non-reusable snapshot");
62        }
63        // In a full implementation, this would update the observer
64        // For now, this is a placeholder
65    }
66
67    pub fn snapshot_id(&self) -> SnapshotId {
68        self.state.id.get()
69    }
70
71    pub fn invalid(&self) -> SnapshotIdSet {
72        self.state.invalid.borrow().clone()
73    }
74
75    pub fn read_only(&self) -> bool {
76        false
77    }
78
79    pub fn root_transparent_mutable(self: &Arc<Self>) -> Arc<Self> {
80        match &self.parent {
81            Some(weak) => weak
82                .upgrade()
83                .map(|parent| parent.root_transparent_mutable())
84                .unwrap_or_else(|| self.clone()),
85            None => self.clone(),
86        }
87    }
88
89    pub fn enter<T>(self: &Arc<Self>, f: impl FnOnce() -> T) -> T {
90        let prev = current_snapshot();
91
92        if let Some(ref snapshot) = prev {
93            if snapshot.is_same_transparent(self) {
94                return f();
95            }
96        }
97
98        set_current_snapshot(Some(AnySnapshot::TransparentMutable(self.clone())));
99        let out = f();
100        set_current_snapshot(prev);
101        out
102    }
103
104    pub fn take_nested_snapshot(
105        &self,
106        read_observer: Option<ReadObserver>,
107    ) -> Arc<ReadonlySnapshot> {
108        let merged_observer = merge_read_observers(read_observer, self.state.read_observer.clone());
109        ReadonlySnapshot::new(
110            self.state.id.get(),
111            self.state.invalid.borrow().clone(),
112            merged_observer,
113        )
114    }
115
116    pub fn has_pending_changes(&self) -> bool {
117        !self.state.modified.borrow().is_empty()
118    }
119
120    pub fn dispose(&self) {
121        if !self.state.disposed.get() && self.nested_count.get() == 0 {
122            self.state.dispose();
123        }
124    }
125
126    pub fn record_read(&self, state: &dyn StateObject) {
127        self.state.record_read(state);
128    }
129
130    pub fn record_write(&self, state: Arc<dyn StateObject>) {
131        if self.applied.get() {
132            panic!("Cannot write to an applied snapshot");
133        }
134        self.state.record_write(state, self.state.id.get());
135    }
136
137    pub fn close(&self) {
138        self.state.disposed.set(true);
139    }
140
141    pub fn is_disposed(&self) -> bool {
142        self.state.disposed.get()
143    }
144
145    pub fn apply(&self) -> SnapshotApplyResult {
146        if self.state.disposed.get() || self.applied.get() {
147            return SnapshotApplyResult::Failure;
148        }
149
150        self.applied.set(true);
151        SnapshotApplyResult::Success
152    }
153
154    pub fn take_nested_mutable_snapshot(
155        &self,
156        read_observer: Option<ReadObserver>,
157        write_observer: Option<WriteObserver>,
158    ) -> Arc<TransparentObserverMutableSnapshot> {
159        let merged_read = merge_read_observers(read_observer, self.state.read_observer.clone());
160        let merged_write = merge_write_observers(write_observer, self.state.write_observer.clone());
161
162        let mut invalid = self.state.invalid.borrow().clone();
163        let new_id = self.state.id.get() + 1;
164        invalid = invalid.set(new_id);
165
166        TransparentObserverMutableSnapshot::new(
167            new_id,
168            invalid,
169            merged_read,
170            merged_write,
171            self.parent.clone(),
172        )
173    }
174}
175
176/// A transparent read-only snapshot.
177///
178/// Similar to TransparentObserverMutableSnapshot but for read-only snapshots.
179///
180/// # Thread Safety
181/// Contains `Cell<T>` and `RefCell<T>` which are not `Send`/`Sync`. This is safe because
182/// snapshots are stored in thread-local storage and never shared across threads. The `Arc`
183/// is used for cheap cloning within a single thread, not for cross-thread sharing.
184#[allow(clippy::arc_with_non_send_sync)]
185pub struct TransparentObserverSnapshot {
186    state: SnapshotState,
187    parent: Option<Weak<TransparentObserverSnapshot>>,
188    reusable: Cell<bool>,
189}
190
191impl TransparentObserverSnapshot {
192    pub fn new(
193        id: SnapshotId,
194        invalid: SnapshotIdSet,
195        read_observer: Option<ReadObserver>,
196        parent: Option<Weak<TransparentObserverSnapshot>>,
197    ) -> Arc<Self> {
198        Arc::new(Self {
199            state: SnapshotState::new(id, invalid, read_observer, None, false),
200            parent,
201            reusable: Cell::new(true),
202        })
203    }
204
205    /// Check if this snapshot can be reused for observer changes.
206    pub fn can_reuse(&self) -> bool {
207        self.reusable.get()
208    }
209
210    /// Set the read observer (only allowed if reusable).
211    pub fn set_read_observer(&self, _observer: Option<ReadObserver>) {
212        if !self.can_reuse() {
213            panic!("Cannot change observers on non-reusable snapshot");
214        }
215        // Placeholder
216    }
217
218    pub fn snapshot_id(&self) -> SnapshotId {
219        self.state.id.get()
220    }
221
222    pub fn invalid(&self) -> SnapshotIdSet {
223        self.state.invalid.borrow().clone()
224    }
225
226    pub fn read_only(&self) -> bool {
227        true
228    }
229
230    pub fn root_transparent_readonly(self: &Arc<Self>) -> Arc<Self> {
231        match &self.parent {
232            Some(weak) => weak
233                .upgrade()
234                .map(|parent| parent.root_transparent_readonly())
235                .unwrap_or_else(|| self.clone()),
236            None => self.clone(),
237        }
238    }
239
240    pub fn enter<T>(self: &Arc<Self>, f: impl FnOnce() -> T) -> T {
241        let previous = current_snapshot();
242
243        if let Some(ref prev_snapshot) = previous {
244            if prev_snapshot.is_same_transparent_readonly(self) {
245                return f();
246            }
247        }
248
249        set_current_snapshot(Some(AnySnapshot::TransparentReadonly(self.clone())));
250        let result = f();
251        set_current_snapshot(previous);
252        result
253    }
254
255    pub fn take_nested_snapshot(
256        &self,
257        read_observer: Option<ReadObserver>,
258    ) -> Arc<TransparentObserverSnapshot> {
259        let merged_observer = merge_read_observers(read_observer, self.state.read_observer.clone());
260        TransparentObserverSnapshot::new(
261            self.state.id.get(),
262            self.state.invalid.borrow().clone(),
263            merged_observer,
264            self.parent.clone(),
265        )
266    }
267
268    pub fn has_pending_changes(&self) -> bool {
269        false
270    }
271
272    pub fn dispose(&self) {
273        self.state.dispose();
274    }
275
276    pub fn record_read(&self, state: &dyn StateObject) {
277        self.state.record_read(state);
278    }
279
280    pub fn record_write(&self, _state: Arc<dyn StateObject>) {
281        panic!("Cannot write to a read-only snapshot");
282    }
283
284    pub fn close(&self) {
285        self.state.disposed.set(true);
286    }
287
288    pub fn is_disposed(&self) -> bool {
289        self.state.disposed.get()
290    }
291}
292
293#[cfg(test)]
294mod tests {
295    use super::*;
296    use crate::snapshot_v2::runtime::TestRuntimeGuard;
297
298    fn reset_runtime() -> TestRuntimeGuard {
299        reset_runtime_for_tests()
300    }
301
302    #[test]
303    fn test_transparent_observer_mutable_snapshot() {
304        let _guard = reset_runtime();
305        let snapshot =
306            TransparentObserverMutableSnapshot::new(1, SnapshotIdSet::new(), None, None, None);
307
308        assert_eq!(snapshot.snapshot_id(), 1);
309        assert!(!snapshot.read_only());
310        assert!(snapshot.can_reuse());
311    }
312
313    #[test]
314    fn test_transparent_observer_mutable_apply() {
315        let _guard = reset_runtime();
316        let snapshot =
317            TransparentObserverMutableSnapshot::new(1, SnapshotIdSet::new(), None, None, None);
318
319        let result = snapshot.apply();
320        assert!(result.is_success());
321    }
322
323    #[test]
324    fn test_transparent_observer_snapshot() {
325        let _guard = reset_runtime();
326        let snapshot = TransparentObserverSnapshot::new(1, SnapshotIdSet::new(), None, None);
327
328        assert_eq!(snapshot.snapshot_id(), 1);
329        assert!(snapshot.read_only());
330        assert!(snapshot.can_reuse());
331    }
332
333    #[test]
334    #[should_panic(expected = "Cannot write to a read-only snapshot")]
335    fn test_transparent_observer_snapshot_write_panics() {
336        use crate::state::StateObject;
337        use std::cell::Cell;
338
339        let _guard = reset_runtime();
340
341        #[allow(dead_code)]
342        struct MockState {
343            value: Cell<i32>,
344        }
345
346        impl StateObject for MockState {
347            fn object_id(&self) -> crate::state::ObjectId {
348                crate::state::ObjectId(0)
349            }
350
351            fn first_record(&self) -> Arc<crate::state::StateRecord> {
352                unimplemented!("Not needed for tests")
353            }
354
355            fn readable_record(
356                &self,
357                _snapshot_id: crate::snapshot_id_set::SnapshotId,
358                _invalid: &SnapshotIdSet,
359            ) -> Arc<crate::state::StateRecord> {
360                unimplemented!("Not needed for tests")
361            }
362
363            fn prepend_state_record(&self, _record: Arc<crate::state::StateRecord>) {
364                unimplemented!("Not needed for tests")
365            }
366
367            fn promote_record(
368                &self,
369                _child_id: crate::snapshot_id_set::SnapshotId,
370            ) -> Result<(), &'static str> {
371                unimplemented!("Not needed for tests")
372            }
373
374            fn as_any(&self) -> &dyn std::any::Any {
375                self
376            }
377        }
378
379        let snapshot = TransparentObserverSnapshot::new(1, SnapshotIdSet::new(), None, None);
380
381        let mock_state = Arc::new(MockState {
382            value: Cell::new(42),
383        });
384        snapshot.record_write(mock_state);
385    }
386
387    #[test]
388    fn test_transparent_observer_mutable_nested() {
389        let _guard = reset_runtime();
390        let parent =
391            TransparentObserverMutableSnapshot::new(1, SnapshotIdSet::new(), None, None, None);
392
393        let nested = parent.take_nested_mutable_snapshot(None, None);
394        assert!(nested.snapshot_id() > parent.snapshot_id());
395    }
396
397    #[test]
398    fn test_transparent_observer_snapshot_nested() {
399        let _guard = reset_runtime();
400        let parent = TransparentObserverSnapshot::new(1, SnapshotIdSet::new(), None, None);
401
402        let nested = parent.take_nested_snapshot(None);
403        assert_eq!(nested.snapshot_id(), parent.snapshot_id());
404        assert!(nested.read_only());
405    }
406}