Skip to main content

cranpose_core/snapshot_v2/
transparent.rs

1use super::*;
2
3/// A transparent mutable snapshot that allows observer replacement.
4///
5/// This snapshot type is optimized for cases where observers need to be
6/// temporarily added or removed without creating a new snapshot structure.
7///
8/// # Thread Safety
9/// Contains `Cell<T>` and `RefCell<T>` which are not `Send`/`Sync`. This is safe because
10/// snapshots are stored in thread-local storage and never shared across threads. The `Arc`
11/// is used for cheap cloning within a single thread, not for cross-thread sharing.
12pub struct TransparentObserverMutableSnapshot {
13    state: SnapshotState,
14    parent: Option<Weak<TransparentObserverMutableSnapshot>>,
15    nested_count: Cell<usize>,
16    applied: Cell<bool>,
17    reusable: Cell<bool>,
18}
19
20impl TransparentObserverMutableSnapshot {
21    pub fn new(
22        id: SnapshotId,
23        invalid: SnapshotIdSet,
24        read_observer: Option<ReadObserver>,
25        write_observer: Option<WriteObserver>,
26        parent: Option<Weak<TransparentObserverMutableSnapshot>>,
27    ) -> Arc<Self> {
28        Self::new_reusing(None, id, invalid, read_observer, write_observer, parent)
29    }
30
31    pub(crate) fn new_reusing(
32        recycled: Option<Arc<Self>>,
33        id: SnapshotId,
34        invalid: SnapshotIdSet,
35        read_observer: Option<ReadObserver>,
36        write_observer: Option<WriteObserver>,
37        parent: Option<Weak<Self>>,
38    ) -> Arc<Self> {
39        let fresh = Self {
40            state: SnapshotState::new_with_pinning(
41                id,
42                invalid,
43                read_observer,
44                write_observer,
45                false,
46                false,
47            ),
48            parent,
49            nested_count: Cell::new(0),
50            applied: Cell::new(false),
51            reusable: Cell::new(true),
52        };
53        if let Some(mut recycled) = recycled
54            && let Some(target) = Arc::get_mut(&mut recycled)
55        {
56            *target = fresh;
57            recycled
58        } else {
59            Arc::new(fresh)
60        }
61    }
62
63    /// Check if this snapshot can be reused for observer changes.
64    pub fn can_reuse(&self) -> bool {
65        self.reusable.get()
66    }
67
68    pub(crate) fn observers(&self) -> (Option<ReadObserver>, Option<WriteObserver>) {
69        (
70            self.state.read_observer.borrow().clone(),
71            self.state.write_observer.borrow().clone(),
72        )
73    }
74
75    pub(crate) fn reads_into(&self, observer: &ReadObserver) -> bool {
76        self.state
77            .read_observer
78            .borrow()
79            .as_ref()
80            .is_some_and(|installed| Arc::ptr_eq(installed, observer))
81    }
82
83    /// Set the read observer (only allowed if reusable).
84    pub fn set_read_observer(&self, observer: Option<ReadObserver>) {
85        assert!(
86            self.can_reuse(),
87            "Cannot change observers on non-reusable snapshot"
88        );
89        *self.state.read_observer.borrow_mut() = observer;
90    }
91
92    /// Set the write observer (only allowed if reusable).
93    pub fn set_write_observer(&self, observer: Option<WriteObserver>) {
94        assert!(
95            self.can_reuse(),
96            "Cannot change observers on non-reusable snapshot"
97        );
98        *self.state.write_observer.borrow_mut() = observer;
99    }
100
101    pub fn snapshot_id(&self) -> SnapshotId {
102        self.state.id.get()
103    }
104
105    pub fn invalid(&self) -> SnapshotIdSet {
106        self.state.invalid.borrow().clone()
107    }
108
109    pub fn read_only(&self) -> bool {
110        false
111    }
112
113    pub fn root_transparent_mutable(self: &Arc<Self>) -> Arc<Self> {
114        match &self.parent {
115            Some(weak) => weak
116                .upgrade()
117                .map_or_else(|| self.clone(), |parent| parent.root_transparent_mutable()),
118            None => self.clone(),
119        }
120    }
121
122    pub fn enter<T>(self: &Arc<Self>, f: impl FnOnce() -> T) -> T {
123        let prev = current_snapshot();
124
125        if let Some(ref snapshot) = prev
126            && snapshot.is_same_transparent(self)
127        {
128            return f();
129        }
130
131        enter_snapshot_scope(AnySnapshot::TransparentMutable(self.clone()), f)
132    }
133
134    pub fn take_nested_snapshot(
135        &self,
136        read_observer: Option<ReadObserver>,
137    ) -> Arc<ReadonlySnapshot> {
138        let merged_observer =
139            merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
140        ReadonlySnapshot::new(
141            self.state.id.get(),
142            self.state.invalid.borrow().clone(),
143            merged_observer,
144        )
145    }
146
147    pub fn has_pending_changes(&self) -> bool {
148        !self.state.modified.borrow().is_empty()
149    }
150
151    pub fn dispose(&self) {
152        if !self.state.disposed.get() && self.nested_count.get() == 0 {
153            self.state.dispose();
154        }
155    }
156
157    pub fn record_read(&self, state: &dyn StateObject) {
158        self.state.record_read(state);
159    }
160
161    pub fn record_write(&self, state: Arc<dyn StateObject>) {
162        assert!(!self.applied.get(), "Cannot write to an applied snapshot");
163        self.state.record_write(state, self.state.id.get());
164    }
165
166    pub fn close(&self) {
167        self.state.disposed.set(true);
168    }
169
170    pub fn is_disposed(&self) -> bool {
171        self.state.disposed.get()
172    }
173
174    pub fn apply(&self) -> SnapshotApplyResult {
175        if self.state.disposed.get() || self.applied.get() {
176            return SnapshotApplyResult::Failure;
177        }
178
179        self.applied.set(true);
180        SnapshotApplyResult::Success
181    }
182
183    pub fn take_nested_mutable_snapshot(
184        &self,
185        read_observer: Option<ReadObserver>,
186        write_observer: Option<WriteObserver>,
187    ) -> Arc<TransparentObserverMutableSnapshot> {
188        let merged_read =
189            merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
190        let merged_write =
191            merge_write_observers(write_observer, self.state.write_observer.borrow().clone());
192
193        let mut invalid = self.state.invalid.borrow().clone();
194        let new_id = self.state.id.get() + 1;
195        invalid = invalid.set(new_id);
196
197        TransparentObserverMutableSnapshot::new(
198            new_id,
199            invalid,
200            merged_read,
201            merged_write,
202            self.parent.clone(),
203        )
204    }
205}
206
207/// A transparent read-only snapshot.
208///
209/// Similar to TransparentObserverMutableSnapshot but for read-only snapshots.
210///
211/// # Thread Safety
212/// Contains `Cell<T>` and `RefCell<T>` which are not `Send`/`Sync`. This is safe because
213/// snapshots are stored in thread-local storage and never shared across threads. The `Arc`
214/// is used for cheap cloning within a single thread, not for cross-thread sharing.
215pub struct TransparentObserverSnapshot {
216    state: SnapshotState,
217    parent: Option<Weak<TransparentObserverSnapshot>>,
218    reusable: Cell<bool>,
219}
220
221impl TransparentObserverSnapshot {
222    pub fn new(
223        id: SnapshotId,
224        invalid: SnapshotIdSet,
225        read_observer: Option<ReadObserver>,
226        parent: Option<Weak<TransparentObserverSnapshot>>,
227    ) -> Arc<Self> {
228        Arc::new(Self {
229            state: SnapshotState::new_with_pinning(id, invalid, read_observer, None, false, false),
230            parent,
231            reusable: Cell::new(true),
232        })
233    }
234
235    /// Check if this snapshot can be reused for observer changes.
236    pub fn can_reuse(&self) -> bool {
237        self.reusable.get()
238    }
239
240    /// Set the read observer (only allowed if reusable).
241    pub fn set_read_observer(&self, observer: Option<ReadObserver>) {
242        assert!(
243            self.can_reuse(),
244            "Cannot change observers on non-reusable snapshot"
245        );
246        *self.state.read_observer.borrow_mut() = observer;
247    }
248
249    pub fn snapshot_id(&self) -> SnapshotId {
250        self.state.id.get()
251    }
252
253    pub fn invalid(&self) -> SnapshotIdSet {
254        self.state.invalid.borrow().clone()
255    }
256
257    pub fn read_only(&self) -> bool {
258        true
259    }
260
261    pub fn root_transparent_readonly(self: &Arc<Self>) -> Arc<Self> {
262        match &self.parent {
263            Some(weak) => weak
264                .upgrade()
265                .map_or_else(|| self.clone(), |parent| parent.root_transparent_readonly()),
266            None => self.clone(),
267        }
268    }
269
270    pub fn enter<T>(self: &Arc<Self>, f: impl FnOnce() -> T) -> T {
271        let previous = current_snapshot();
272
273        if let Some(ref prev_snapshot) = previous
274            && prev_snapshot.is_same_transparent_readonly(self)
275        {
276            return f();
277        }
278
279        enter_snapshot_scope(AnySnapshot::TransparentReadonly(self.clone()), f)
280    }
281
282    pub fn take_nested_snapshot(
283        &self,
284        read_observer: Option<ReadObserver>,
285    ) -> Arc<TransparentObserverSnapshot> {
286        let merged_observer =
287            merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
288        TransparentObserverSnapshot::new(
289            self.state.id.get(),
290            self.state.invalid.borrow().clone(),
291            merged_observer,
292            self.parent.clone(),
293        )
294    }
295
296    pub fn has_pending_changes(&self) -> bool {
297        false
298    }
299
300    pub fn dispose(&self) {
301        self.state.dispose();
302    }
303
304    pub fn record_read(&self, state: &dyn StateObject) {
305        self.state.record_read(state);
306    }
307
308    pub fn record_write(&self, _state: Arc<dyn StateObject>) {
309        panic!("Cannot write to a read-only snapshot");
310    }
311
312    pub fn close(&self) {
313        self.state.disposed.set(true);
314    }
315
316    pub fn is_disposed(&self) -> bool {
317        self.state.disposed.get()
318    }
319}
320
321#[cfg(test)]
322#[path = "tests/transparent_tests.rs"]
323mod tests;