Skip to main content

cranpose_core/snapshot_v2/
nested.rs

1use super::*;
2
3/// A nested read-only snapshot.
4///
5/// This is a read-only snapshot that has a parent snapshot. It inherits
6/// the parent's invalid set and can be disposed independently.
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 NestedReadonlySnapshot {
13    pub(super) state: SnapshotState,
14    parent: Weak<NestedReadonlySnapshot>,
15}
16
17impl NestedReadonlySnapshot {
18    /// Creates a nested read-only snapshot with the given id and invalid set. `read_observer` is
19    /// called for reads inside it. `parent` is the snapshot it was taken from, if that snapshot is
20    /// still alive.
21    pub fn new(
22        id: SnapshotId,
23        invalid: SnapshotIdSet,
24        read_observer: Option<ReadObserver>,
25        parent: Weak<NestedReadonlySnapshot>,
26    ) -> Rc<Self> {
27        Rc::new(Self {
28            state: SnapshotState::new(id, invalid, read_observer, None, false),
29            parent,
30        })
31    }
32
33    /// Returns the id of this snapshot. Ids grow with every snapshot taken.
34    pub fn snapshot_id(&self) -> SnapshotId {
35        self.state.id.get()
36    }
37
38    /// Returns the ids of snapshots whose writes this snapshot does not see.
39    pub fn invalid(&self) -> SnapshotIdSet {
40        self.state.invalid.borrow().clone()
41    }
42
43    /// Returns `true`: this snapshot rejects writes.
44    pub fn read_only(&self) -> bool {
45        true
46    }
47
48    /// Returns the outermost parent that is still alive, or a copy of this snapshot when it has no
49    /// live parent.
50    pub fn root_nested_readonly(&self) -> Rc<NestedReadonlySnapshot> {
51        if let Some(parent) = self.parent.upgrade() {
52            parent.root_nested_readonly()
53        } else {
54            NestedReadonlySnapshot::new(
55                self.state.id.get(),
56                self.state.invalid.borrow().clone(),
57                self.state.read_observer.borrow().clone(),
58                Weak::new(),
59            )
60        }
61    }
62
63    /// Runs `f` with this snapshot as the current snapshot of the thread and returns its result.
64    /// The previous current snapshot is restored when `f` returns.
65    pub fn enter<T>(self: &Rc<Self>, f: impl FnOnce() -> T) -> T {
66        enter_snapshot_scope(AnySnapshot::NestedReadonly(self.clone()), f)
67    }
68
69    /// Takes another read-only snapshot with the same view of state. Reads inside the new snapshot
70    /// call both `read_observer` and this snapshot's own read observer.
71    pub fn take_nested_snapshot(
72        &self,
73        read_observer: Option<ReadObserver>,
74    ) -> Rc<NestedReadonlySnapshot> {
75        let merged_observer =
76            merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
77
78        NestedReadonlySnapshot::new(
79            self.state.id.get(),
80            self.state.invalid.borrow().clone(),
81            merged_observer,
82            self.parent.clone(),
83        )
84    }
85
86    /// Returns `false`: a read-only snapshot has no writes.
87    pub fn has_pending_changes(&self) -> bool {
88        false
89    }
90
91    /// Releases the snapshot. Does nothing if it was already disposed.
92    pub fn dispose(&self) {
93        if !self.state.disposed.get() {
94            self.state.dispose();
95        }
96    }
97
98    /// Calls the read observer, if any, with `state`.
99    pub fn record_read(&self, state: &dyn StateObject) {
100        self.state.record_read(state);
101    }
102
103    /// Always panics: a read-only snapshot rejects writes.
104    pub fn record_write(&self, _state: Rc<dyn StateObject>) {
105        panic!("Cannot write to a read-only snapshot");
106    }
107
108    /// Returns `true` once the snapshot was disposed.
109    pub fn is_disposed(&self) -> bool {
110        self.state.disposed.get()
111    }
112}
113
114/// A nested mutable snapshot.
115///
116/// This is a mutable snapshot that has a parent. Changes made in this
117/// snapshot are applied to the parent when `apply()` is called, not
118/// to the global snapshot.
119///
120/// # Thread Safety
121/// Contains `Cell<T>` and `RefCell<T>` which are not `Send`/`Sync`. This is safe because
122/// snapshots are stored in thread-local storage and never shared across threads. The `Rc`
123/// is used for cheap cloning within a single thread, not for cross-thread sharing.
124pub struct NestedMutableSnapshot {
125    pub(super) state: SnapshotState,
126    parent: Weak<MutableSnapshot>,
127    nested_count: Cell<usize>,
128    applied: Cell<bool>,
129    base_parent_id: SnapshotId,
130}
131
132impl NestedMutableSnapshot {
133    /// Creates a nested mutable snapshot with the given id and invalid set. `parent` receives the
134    /// writes on `apply`. `base_parent_id` is the id of the parent when this snapshot was taken.
135    pub fn new(
136        id: SnapshotId,
137        invalid: SnapshotIdSet,
138        read_observer: Option<ReadObserver>,
139        write_observer: Option<WriteObserver>,
140        parent: Weak<MutableSnapshot>,
141        base_parent_id: SnapshotId,
142    ) -> Rc<Self> {
143        Rc::new(Self {
144            state: SnapshotState::new(id, invalid, read_observer, write_observer, true),
145            parent,
146            nested_count: Cell::new(0),
147            applied: Cell::new(false),
148            base_parent_id,
149        })
150    }
151
152    /// Returns the id of this snapshot. Ids grow with every snapshot taken.
153    pub fn snapshot_id(&self) -> SnapshotId {
154        self.state.id.get()
155    }
156
157    /// Returns the ids of snapshots whose writes this snapshot does not see.
158    pub fn invalid(&self) -> SnapshotIdSet {
159        self.state.invalid.borrow().clone()
160    }
161
162    /// Returns `false`: this snapshot accepts writes.
163    pub fn read_only(&self) -> bool {
164        false
165    }
166
167    pub(crate) fn set_on_dispose<F>(&self, f: F)
168    where
169        F: FnOnce() + 'static,
170    {
171        self.state.set_on_dispose(f);
172    }
173
174    /// Returns the root mutable snapshot above this one. If the root is gone, returns a new root
175    /// snapshot built from this snapshot's id, invalid set and observers.
176    pub fn root_mutable(&self) -> Rc<MutableSnapshot> {
177        if let Some(parent) = self.parent.upgrade() {
178            parent.root_mutable()
179        } else {
180            MutableSnapshot::new(
181                self.state.id.get(),
182                self.state.invalid.borrow().clone(),
183                self.state.read_observer.borrow().clone(),
184                self.state.write_observer.borrow().clone(),
185                self.base_parent_id,
186            )
187        }
188    }
189
190    /// Runs `f` with this snapshot as the current snapshot of the thread and returns its result.
191    /// The previous current snapshot is restored when `f` returns.
192    pub fn enter<T>(self: &Rc<Self>, f: impl FnOnce() -> T) -> T {
193        enter_snapshot_scope(AnySnapshot::NestedMutable(self.clone()), f)
194    }
195
196    /// Takes a read-only snapshot of this snapshot's view of state. Reads inside the new snapshot
197    /// call both `read_observer` and this snapshot's own read observer.
198    pub fn take_nested_snapshot(
199        &self,
200        read_observer: Option<ReadObserver>,
201    ) -> Rc<ReadonlySnapshot> {
202        let merged_observer =
203            merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
204
205        ReadonlySnapshot::new(
206            self.state.id.get(),
207            self.state.invalid.borrow().clone(),
208            merged_observer,
209        )
210    }
211
212    /// Returns `true` when this snapshot recorded writes that were not applied yet.
213    pub fn has_pending_changes(&self) -> bool {
214        !self.state.modified.borrow().is_empty()
215    }
216
217    /// Returns the ids of nested mutable snapshots taken from this snapshot and not yet disposed.
218    pub fn pending_children(&self) -> Vec<SnapshotId> {
219        self.state.pending_children()
220    }
221
222    /// Returns `true` when a nested mutable snapshot taken from this snapshot is still open.
223    pub fn has_pending_children(&self) -> bool {
224        self.state.has_pending_children()
225    }
226
227    /// Discards the writes that were not applied and releases the snapshot. Does nothing while
228    /// nested snapshots taken from it are still open.
229    pub fn dispose(&self) {
230        if !self.state.disposed.get() && self.nested_count.get() == 0 {
231            self.state.dispose();
232        }
233    }
234
235    /// Calls the read observer, if any, with `state`.
236    pub fn record_read(&self, state: &dyn StateObject) {
237        self.state.record_read(state);
238    }
239
240    /// Records `state` as written in this snapshot. The write observer, if any, is called the first
241    /// time a state object is recorded. Panics if the snapshot was applied or disposed.
242    pub fn record_write(&self, state: Rc<dyn StateObject>) {
243        assert!(!self.applied.get(), "Cannot write to an applied snapshot");
244        assert!(
245            !self.state.disposed.get(),
246            "Cannot write to a disposed snapshot"
247        );
248        self.state.record_write(state, self.state.id.get());
249    }
250
251    /// Returns `true` once the snapshot was disposed.
252    pub fn is_disposed(&self) -> bool {
253        self.state.disposed.get()
254    }
255
256    /// Moves the writes of this snapshot into its parent. Returns `Failure` if the snapshot was
257    /// already applied or disposed, if the parent is gone, or if the parent wrote one of the same
258    /// state objects.
259    pub fn apply(&self) -> SnapshotApplyResult {
260        if self.state.disposed.get() {
261            return SnapshotApplyResult::Failure;
262        }
263
264        if self.applied.get() {
265            return SnapshotApplyResult::Failure;
266        }
267
268        if let Some(parent) = self.parent.upgrade() {
269            let child_modified = self.state.modified.borrow();
270            if child_modified.is_empty() {
271                self.applied.set(true);
272                self.state.dispose();
273                return SnapshotApplyResult::Success;
274            }
275            if parent.merge_child_modifications(&child_modified).is_err() {
276                return SnapshotApplyResult::Failure;
277            }
278
279            self.applied.set(true);
280            self.state.dispose();
281            SnapshotApplyResult::Success
282        } else {
283            SnapshotApplyResult::Failure
284        }
285    }
286
287    /// Takes a mutable snapshot nested in this one. Its `apply` moves the writes into this
288    /// snapshot.
289    pub fn take_nested_mutable_snapshot(
290        self: &Rc<Self>,
291        read_observer: Option<ReadObserver>,
292        write_observer: Option<WriteObserver>,
293    ) -> Rc<NestedMutableSnapshot> {
294        let root = Rc::downgrade(&self.root_mutable());
295        allocate_nested_mutable_snapshot(self, root, read_observer, write_observer)
296    }
297}
298
299impl NestedMutableHost for NestedMutableSnapshot {
300    fn snapshot_state(&self) -> &SnapshotState {
301        &self.state
302    }
303
304    fn nested_count(&self) -> &Cell<usize> {
305        &self.nested_count
306    }
307}
308
309#[cfg(test)]
310#[path = "tests/nested_tests.rs"]
311mod tests;