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 `Rc`
11/// is used for cheap cloning within a single thread, not for cross-thread sharing.
12pub struct TransparentObserverMutableSnapshot {
13    pub(super) 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    /// Creates a transparent mutable snapshot with the given id and invalid set. The observers are
22    /// called for reads and writes inside it and can be replaced later. `parent` is the snapshot it
23    /// was taken from, if that snapshot is still alive.
24    pub fn new(
25        id: SnapshotId,
26        invalid: SnapshotIdSet,
27        read_observer: Option<ReadObserver>,
28        write_observer: Option<WriteObserver>,
29        parent: Option<Weak<TransparentObserverMutableSnapshot>>,
30    ) -> Rc<Self> {
31        Self::new_reusing(None, id, invalid, read_observer, write_observer, parent)
32    }
33
34    pub(crate) fn new_reusing(
35        recycled: Option<Rc<Self>>,
36        id: SnapshotId,
37        invalid: SnapshotIdSet,
38        read_observer: Option<ReadObserver>,
39        write_observer: Option<WriteObserver>,
40        parent: Option<Weak<Self>>,
41    ) -> Rc<Self> {
42        let fresh = Self {
43            state: SnapshotState::new_with_pinning(
44                id,
45                invalid,
46                read_observer,
47                write_observer,
48                false,
49                false,
50            ),
51            parent,
52            nested_count: Cell::new(0),
53            applied: Cell::new(false),
54            reusable: Cell::new(true),
55        };
56        if let Some(mut recycled) = recycled
57            && let Some(target) = Rc::get_mut(&mut recycled)
58        {
59            *target = fresh;
60            recycled
61        } else {
62            Rc::new(fresh)
63        }
64    }
65
66    /// Check if this snapshot can be reused for observer changes.
67    pub fn can_reuse(&self) -> bool {
68        self.reusable.get()
69    }
70
71    pub(crate) fn observers(&self) -> (Option<ReadObserver>, Option<WriteObserver>) {
72        (
73            self.state.read_observer.borrow().clone(),
74            self.state.write_observer.borrow().clone(),
75        )
76    }
77
78    pub(crate) fn reads_into(&self, observer: &ReadObserver) -> bool {
79        self.state
80            .read_observer
81            .borrow()
82            .as_ref()
83            .is_some_and(|installed| Rc::ptr_eq(installed, observer))
84    }
85
86    /// Set the read observer (only allowed if reusable).
87    pub fn set_read_observer(&self, observer: Option<ReadObserver>) {
88        assert!(
89            self.can_reuse(),
90            "Cannot change observers on non-reusable snapshot"
91        );
92        *self.state.read_observer.borrow_mut() = observer;
93    }
94
95    /// Set the write observer (only allowed if reusable).
96    pub fn set_write_observer(&self, observer: Option<WriteObserver>) {
97        assert!(
98            self.can_reuse(),
99            "Cannot change observers on non-reusable snapshot"
100        );
101        *self.state.write_observer.borrow_mut() = observer;
102    }
103
104    /// Returns the id of this snapshot. Ids grow with every snapshot taken.
105    pub fn snapshot_id(&self) -> SnapshotId {
106        self.state.id.get()
107    }
108
109    /// Returns the ids of snapshots whose writes this snapshot does not see.
110    pub fn invalid(&self) -> SnapshotIdSet {
111        self.state.invalid.borrow().clone()
112    }
113
114    /// Returns `false`: this snapshot accepts writes.
115    pub fn read_only(&self) -> bool {
116        false
117    }
118
119    /// Returns the outermost parent that is still alive, or this snapshot when it has none.
120    pub fn root_transparent_mutable(self: &Rc<Self>) -> Rc<Self> {
121        match &self.parent {
122            Some(weak) => weak
123                .upgrade()
124                .map_or_else(|| self.clone(), |parent| parent.root_transparent_mutable()),
125            None => self.clone(),
126        }
127    }
128
129    /// Runs `f` with this snapshot as the current snapshot of the thread and returns its result. If
130    /// this snapshot is already current, `f` runs directly. The previous current snapshot is
131    /// restored when `f` returns.
132    pub fn enter<T>(self: &Rc<Self>, f: impl FnOnce() -> T) -> T {
133        let prev = current_snapshot();
134
135        if let Some(ref snapshot) = prev
136            && snapshot.is_same_transparent(self)
137        {
138            return f();
139        }
140
141        enter_snapshot_scope(AnySnapshot::TransparentMutable(self.clone()), f)
142    }
143
144    /// Takes a read-only snapshot of this snapshot's view of state. Reads inside the new snapshot
145    /// call both `read_observer` and this snapshot's own read observer.
146    pub fn take_nested_snapshot(
147        &self,
148        read_observer: Option<ReadObserver>,
149    ) -> Rc<ReadonlySnapshot> {
150        let merged_observer =
151            merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
152        ReadonlySnapshot::new(
153            self.state.id.get(),
154            self.state.invalid.borrow().clone(),
155            merged_observer,
156        )
157    }
158
159    /// Returns `true` when this snapshot recorded writes that were not applied yet.
160    pub fn has_pending_changes(&self) -> bool {
161        !self.state.modified.borrow().is_empty()
162    }
163
164    /// Releases the snapshot. Does nothing if it was already disposed or if nested snapshots taken
165    /// from it are still open.
166    pub fn dispose(&self) {
167        if !self.state.disposed.get() && self.nested_count.get() == 0 {
168            self.state.dispose();
169        }
170    }
171
172    /// Calls the read observer, if any, with `state`.
173    pub fn record_read(&self, state: &dyn StateObject) {
174        self.state.record_read(state);
175    }
176
177    /// Records `state` as written in this snapshot. The write observer, if any, is called the first
178    /// time a state object is recorded. Panics if the snapshot was applied.
179    pub fn record_write(&self, state: Rc<dyn StateObject>) {
180        assert!(!self.applied.get(), "Cannot write to an applied snapshot");
181        self.state.record_write(state, self.state.id.get());
182    }
183
184    /// Returns `true` once the snapshot was disposed.
185    pub fn is_disposed(&self) -> bool {
186        self.state.disposed.get()
187    }
188
189    /// Marks the snapshot as applied. Returns `Failure` if it was already applied or disposed.
190    pub fn apply(&self) -> SnapshotApplyResult {
191        if self.state.disposed.get() || self.applied.get() {
192            return SnapshotApplyResult::Failure;
193        }
194
195        self.applied.set(true);
196        SnapshotApplyResult::Success
197    }
198
199    /// Takes a transparent mutable snapshot nested in this one. Reads and writes inside it call
200    /// both the given observers and this snapshot's own observers.
201    pub fn take_nested_mutable_snapshot(
202        &self,
203        read_observer: Option<ReadObserver>,
204        write_observer: Option<WriteObserver>,
205    ) -> Rc<TransparentObserverMutableSnapshot> {
206        let merged_read =
207            merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
208        let merged_write =
209            merge_write_observers(write_observer, self.state.write_observer.borrow().clone());
210
211        let mut invalid = self.state.invalid.borrow().clone();
212        let new_id = self.state.id.get() + 1;
213        invalid = invalid.set(new_id);
214
215        TransparentObserverMutableSnapshot::new(
216            new_id,
217            invalid,
218            merged_read,
219            merged_write,
220            self.parent.clone(),
221        )
222    }
223}
224
225/// A transparent read-only snapshot.
226///
227/// Similar to TransparentObserverMutableSnapshot but for read-only snapshots.
228///
229/// # Thread Safety
230/// Contains `Cell<T>` and `RefCell<T>` which are not `Send`/`Sync`. This is safe because
231/// snapshots are stored in thread-local storage and never shared across threads. The `Rc`
232/// is used for cheap cloning within a single thread, not for cross-thread sharing.
233pub struct TransparentObserverSnapshot {
234    pub(super) state: SnapshotState,
235    parent: Option<Weak<TransparentObserverSnapshot>>,
236    reusable: Cell<bool>,
237}
238
239impl TransparentObserverSnapshot {
240    /// Creates a transparent read-only snapshot with the given id and invalid set. `read_observer`
241    /// is called for reads inside it and can be replaced later. `parent` is the snapshot it was
242    /// taken from, if that snapshot is still alive.
243    pub fn new(
244        id: SnapshotId,
245        invalid: SnapshotIdSet,
246        read_observer: Option<ReadObserver>,
247        parent: Option<Weak<TransparentObserverSnapshot>>,
248    ) -> Rc<Self> {
249        Rc::new(Self {
250            state: SnapshotState::new_with_pinning(id, invalid, read_observer, None, false, false),
251            parent,
252            reusable: Cell::new(true),
253        })
254    }
255
256    /// Check if this snapshot can be reused for observer changes.
257    pub fn can_reuse(&self) -> bool {
258        self.reusable.get()
259    }
260
261    /// Set the read observer (only allowed if reusable).
262    pub fn set_read_observer(&self, observer: Option<ReadObserver>) {
263        assert!(
264            self.can_reuse(),
265            "Cannot change observers on non-reusable snapshot"
266        );
267        *self.state.read_observer.borrow_mut() = observer;
268    }
269
270    /// Returns the id of this snapshot. Ids grow with every snapshot taken.
271    pub fn snapshot_id(&self) -> SnapshotId {
272        self.state.id.get()
273    }
274
275    /// Returns the ids of snapshots whose writes this snapshot does not see.
276    pub fn invalid(&self) -> SnapshotIdSet {
277        self.state.invalid.borrow().clone()
278    }
279
280    /// Returns `true`: this snapshot rejects writes.
281    pub fn read_only(&self) -> bool {
282        true
283    }
284
285    /// Returns the outermost parent that is still alive, or this snapshot when it has none.
286    pub fn root_transparent_readonly(self: &Rc<Self>) -> Rc<Self> {
287        match &self.parent {
288            Some(weak) => weak
289                .upgrade()
290                .map_or_else(|| self.clone(), |parent| parent.root_transparent_readonly()),
291            None => self.clone(),
292        }
293    }
294
295    /// Runs `f` with this snapshot as the current snapshot of the thread and returns its result. If
296    /// this snapshot is already current, `f` runs directly. The previous current snapshot is
297    /// restored when `f` returns.
298    pub fn enter<T>(self: &Rc<Self>, f: impl FnOnce() -> T) -> T {
299        let previous = current_snapshot();
300
301        if let Some(ref prev_snapshot) = previous
302            && prev_snapshot.is_same_transparent_readonly(self)
303        {
304            return f();
305        }
306
307        enter_snapshot_scope(AnySnapshot::TransparentReadonly(self.clone()), f)
308    }
309
310    /// Takes another transparent read-only snapshot with the same view of state. Reads inside the
311    /// new snapshot call both `read_observer` and this snapshot's own read observer.
312    pub fn take_nested_snapshot(
313        &self,
314        read_observer: Option<ReadObserver>,
315    ) -> Rc<TransparentObserverSnapshot> {
316        let merged_observer =
317            merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
318        TransparentObserverSnapshot::new(
319            self.state.id.get(),
320            self.state.invalid.borrow().clone(),
321            merged_observer,
322            self.parent.clone(),
323        )
324    }
325
326    /// Returns `false`: a read-only snapshot has no writes.
327    pub fn has_pending_changes(&self) -> bool {
328        false
329    }
330
331    /// Releases the snapshot. Call it once the snapshot is no longer needed.
332    pub fn dispose(&self) {
333        self.state.dispose();
334    }
335
336    /// Calls the read observer, if any, with `state`.
337    pub fn record_read(&self, state: &dyn StateObject) {
338        self.state.record_read(state);
339    }
340
341    /// Always panics: a read-only snapshot rejects writes.
342    pub fn record_write(&self, _state: Rc<dyn StateObject>) {
343        panic!("Cannot write to a read-only snapshot");
344    }
345
346    /// Returns `true` once the snapshot was disposed.
347    pub fn is_disposed(&self) -> bool {
348        self.state.disposed.get()
349    }
350}
351
352#[cfg(test)]
353#[path = "tests/transparent_tests.rs"]
354mod tests;