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    /// Set the read observer (only allowed if reusable).
76    pub fn set_read_observer(&self, observer: Option<ReadObserver>) {
77        assert!(
78            self.can_reuse(),
79            "Cannot change observers on non-reusable snapshot"
80        );
81        *self.state.read_observer.borrow_mut() = observer;
82    }
83
84    /// Set the write observer (only allowed if reusable).
85    pub fn set_write_observer(&self, observer: Option<WriteObserver>) {
86        assert!(
87            self.can_reuse(),
88            "Cannot change observers on non-reusable snapshot"
89        );
90        *self.state.write_observer.borrow_mut() = observer;
91    }
92
93    pub fn snapshot_id(&self) -> SnapshotId {
94        self.state.id.get()
95    }
96
97    pub fn invalid(&self) -> SnapshotIdSet {
98        self.state.invalid.borrow().clone()
99    }
100
101    pub fn read_only(&self) -> bool {
102        false
103    }
104
105    pub fn root_transparent_mutable(self: &Arc<Self>) -> Arc<Self> {
106        match &self.parent {
107            Some(weak) => weak
108                .upgrade()
109                .map_or_else(|| self.clone(), |parent| parent.root_transparent_mutable()),
110            None => self.clone(),
111        }
112    }
113
114    pub fn enter<T>(self: &Arc<Self>, f: impl FnOnce() -> T) -> T {
115        let prev = current_snapshot();
116
117        if let Some(ref snapshot) = prev
118            && snapshot.is_same_transparent(self)
119        {
120            return f();
121        }
122
123        enter_snapshot_scope(AnySnapshot::TransparentMutable(self.clone()), f)
124    }
125
126    pub fn take_nested_snapshot(
127        &self,
128        read_observer: Option<ReadObserver>,
129    ) -> Arc<ReadonlySnapshot> {
130        let merged_observer =
131            merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
132        ReadonlySnapshot::new(
133            self.state.id.get(),
134            self.state.invalid.borrow().clone(),
135            merged_observer,
136        )
137    }
138
139    pub fn has_pending_changes(&self) -> bool {
140        !self.state.modified.borrow().is_empty()
141    }
142
143    pub fn dispose(&self) {
144        if !self.state.disposed.get() && self.nested_count.get() == 0 {
145            self.state.dispose();
146        }
147    }
148
149    pub fn record_read(&self, state: &dyn StateObject) {
150        self.state.record_read(state);
151    }
152
153    pub fn record_write(&self, state: Arc<dyn StateObject>) {
154        assert!(!self.applied.get(), "Cannot write to an applied snapshot");
155        self.state.record_write(state, self.state.id.get());
156    }
157
158    pub fn close(&self) {
159        self.state.disposed.set(true);
160    }
161
162    pub fn is_disposed(&self) -> bool {
163        self.state.disposed.get()
164    }
165
166    pub fn apply(&self) -> SnapshotApplyResult {
167        if self.state.disposed.get() || self.applied.get() {
168            return SnapshotApplyResult::Failure;
169        }
170
171        self.applied.set(true);
172        SnapshotApplyResult::Success
173    }
174
175    pub fn take_nested_mutable_snapshot(
176        &self,
177        read_observer: Option<ReadObserver>,
178        write_observer: Option<WriteObserver>,
179    ) -> Arc<TransparentObserverMutableSnapshot> {
180        let merged_read =
181            merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
182        let merged_write =
183            merge_write_observers(write_observer, self.state.write_observer.borrow().clone());
184
185        let mut invalid = self.state.invalid.borrow().clone();
186        let new_id = self.state.id.get() + 1;
187        invalid = invalid.set(new_id);
188
189        TransparentObserverMutableSnapshot::new(
190            new_id,
191            invalid,
192            merged_read,
193            merged_write,
194            self.parent.clone(),
195        )
196    }
197}
198
199/// A transparent read-only snapshot.
200///
201/// Similar to TransparentObserverMutableSnapshot but for read-only snapshots.
202///
203/// # Thread Safety
204/// Contains `Cell<T>` and `RefCell<T>` which are not `Send`/`Sync`. This is safe because
205/// snapshots are stored in thread-local storage and never shared across threads. The `Arc`
206/// is used for cheap cloning within a single thread, not for cross-thread sharing.
207pub struct TransparentObserverSnapshot {
208    state: SnapshotState,
209    parent: Option<Weak<TransparentObserverSnapshot>>,
210    reusable: Cell<bool>,
211}
212
213impl TransparentObserverSnapshot {
214    pub fn new(
215        id: SnapshotId,
216        invalid: SnapshotIdSet,
217        read_observer: Option<ReadObserver>,
218        parent: Option<Weak<TransparentObserverSnapshot>>,
219    ) -> Arc<Self> {
220        Arc::new(Self {
221            state: SnapshotState::new_with_pinning(id, invalid, read_observer, None, false, false),
222            parent,
223            reusable: Cell::new(true),
224        })
225    }
226
227    /// Check if this snapshot can be reused for observer changes.
228    pub fn can_reuse(&self) -> bool {
229        self.reusable.get()
230    }
231
232    /// Set the read observer (only allowed if reusable).
233    pub fn set_read_observer(&self, observer: Option<ReadObserver>) {
234        assert!(
235            self.can_reuse(),
236            "Cannot change observers on non-reusable snapshot"
237        );
238        *self.state.read_observer.borrow_mut() = observer;
239    }
240
241    pub fn snapshot_id(&self) -> SnapshotId {
242        self.state.id.get()
243    }
244
245    pub fn invalid(&self) -> SnapshotIdSet {
246        self.state.invalid.borrow().clone()
247    }
248
249    pub fn read_only(&self) -> bool {
250        true
251    }
252
253    pub fn root_transparent_readonly(self: &Arc<Self>) -> Arc<Self> {
254        match &self.parent {
255            Some(weak) => weak
256                .upgrade()
257                .map_or_else(|| self.clone(), |parent| parent.root_transparent_readonly()),
258            None => self.clone(),
259        }
260    }
261
262    pub fn enter<T>(self: &Arc<Self>, f: impl FnOnce() -> T) -> T {
263        let previous = current_snapshot();
264
265        if let Some(ref prev_snapshot) = previous
266            && prev_snapshot.is_same_transparent_readonly(self)
267        {
268            return f();
269        }
270
271        enter_snapshot_scope(AnySnapshot::TransparentReadonly(self.clone()), f)
272    }
273
274    pub fn take_nested_snapshot(
275        &self,
276        read_observer: Option<ReadObserver>,
277    ) -> Arc<TransparentObserverSnapshot> {
278        let merged_observer =
279            merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
280        TransparentObserverSnapshot::new(
281            self.state.id.get(),
282            self.state.invalid.borrow().clone(),
283            merged_observer,
284            self.parent.clone(),
285        )
286    }
287
288    pub fn has_pending_changes(&self) -> bool {
289        false
290    }
291
292    pub fn dispose(&self) {
293        self.state.dispose();
294    }
295
296    pub fn record_read(&self, state: &dyn StateObject) {
297        self.state.record_read(state);
298    }
299
300    pub fn record_write(&self, _state: Arc<dyn StateObject>) {
301        panic!("Cannot write to a read-only snapshot");
302    }
303
304    pub fn close(&self) {
305        self.state.disposed.set(true);
306    }
307
308    pub fn is_disposed(&self) -> bool {
309        self.state.disposed.get()
310    }
311}
312
313#[cfg(test)]
314#[path = "tests/transparent_tests.rs"]
315mod tests;