Skip to main content

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