cranpose_core/snapshot_v2/
readonly.rs1use super::*;
2
3#[allow(clippy::arc_with_non_send_sync)]
13pub struct ReadonlySnapshot {
14 state: SnapshotState,
15}
16
17impl ReadonlySnapshot {
18 pub fn new(
20 id: SnapshotId,
21 invalid: SnapshotIdSet,
22 read_observer: Option<ReadObserver>,
23 ) -> Arc<Self> {
24 Arc::new(Self {
25 state: SnapshotState::new(id, invalid, read_observer, None, false),
26 })
27 }
28
29 pub fn snapshot_id(&self) -> SnapshotId {
30 self.state.id.get()
31 }
32
33 pub fn invalid(&self) -> SnapshotIdSet {
34 self.state.invalid.borrow().clone()
35 }
36
37 pub fn read_only(&self) -> bool {
38 true
39 }
40
41 pub fn root_readonly(&self) -> Arc<Self> {
42 ReadonlySnapshot::new(
43 self.state.id.get(),
44 self.state.invalid.borrow().clone(),
45 self.state.read_observer.borrow().clone(),
46 )
47 }
48
49 pub fn enter<T>(&self, f: impl FnOnce() -> T) -> T {
50 enter_snapshot_scope(AnySnapshot::Readonly(self.root_readonly()), f)
51 }
52
53 pub fn take_nested_snapshot(&self, read_observer: Option<ReadObserver>) -> Arc<Self> {
54 let merged_observer =
55 merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
56 ReadonlySnapshot::new(
57 self.state.id.get(),
58 self.state.invalid.borrow().clone(),
59 merged_observer,
60 )
61 }
62
63 pub fn has_pending_changes(&self) -> bool {
64 false
65 }
66
67 pub fn dispose(&self) {
68 self.state.dispose();
69 }
70
71 pub fn record_read(&self, state: &dyn StateObject) {
72 self.state.record_read(state);
73 }
74
75 pub fn record_write(&self, _state: Arc<dyn StateObject>) {
76 panic!("Cannot write to a read-only snapshot");
77 }
78
79 pub fn is_disposed(&self) -> bool {
80 self.state.disposed.get()
81 }
82
83 pub(crate) fn set_on_dispose<F>(&self, f: F)
84 where
85 F: FnOnce() + 'static,
86 {
87 self.state.set_on_dispose(f);
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use std::rc::Rc;
94
95 use super::*;
96 use crate::state::{PREEXISTING_SNAPSHOT_ID, StateObject};
97
98 fn mock_state_record() -> Rc<crate::state::StateRecord> {
99 crate::state::StateRecord::new(PREEXISTING_SNAPSHOT_ID, (), None)
100 }
101
102 struct MockStateObject;
103
104 impl StateObject for MockStateObject {
105 fn object_id(&self) -> crate::state::ObjectId {
106 crate::state::ObjectId(0)
107 }
108
109 fn first_record(&self) -> Rc<crate::state::StateRecord> {
110 mock_state_record()
111 }
112
113 fn try_readable_record(
114 &self,
115 snapshot_id: crate::snapshot_id_set::SnapshotId,
116 invalid: &SnapshotIdSet,
117 ) -> Option<Rc<crate::state::StateRecord>> {
118 Some(self.readable_record(snapshot_id, invalid))
119 }
120
121 fn readable_record(
122 &self,
123 _snapshot_id: crate::snapshot_id_set::SnapshotId,
124 _invalid: &SnapshotIdSet,
125 ) -> Rc<crate::state::StateRecord> {
126 mock_state_record()
127 }
128
129 fn prepend_state_record(&self, _record: Rc<crate::state::StateRecord>) {}
130
131 fn promote_record(
132 &self,
133 _child_id: crate::snapshot_id_set::SnapshotId,
134 ) -> Result<(), &'static str> {
135 Ok(())
136 }
137
138 fn as_any(&self) -> &dyn std::any::Any {
139 self
140 }
141 }
142
143 #[test]
144 fn test_readonly_snapshot_creation() {
145 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
146 assert_eq!(snapshot.snapshot_id(), 1);
147 assert!(!snapshot.is_disposed());
148 }
149
150 #[test]
151 fn test_readonly_snapshot_is_valid() {
152 let invalid = SnapshotIdSet::new().set(5);
153 let snapshot = ReadonlySnapshot::new(10, invalid, None);
154
155 let any_snapshot = AnySnapshot::Readonly(snapshot.clone());
156 assert!(any_snapshot.is_valid(1));
157 assert!(any_snapshot.is_valid(10));
158 assert!(!any_snapshot.is_valid(5));
159 assert!(!any_snapshot.is_valid(11));
160 }
161
162 #[test]
163 fn test_readonly_snapshot_no_pending_changes() {
164 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
165 assert!(!snapshot.has_pending_changes());
166 }
167
168 #[test]
169 fn test_readonly_snapshot_enter() {
170 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
171
172 set_current_snapshot(None);
173 assert!(current_snapshot().is_none());
174
175 snapshot.enter(|| {
176 let current = current_snapshot();
177 assert!(current.is_some());
178 assert_eq!(current.unwrap().snapshot_id(), 1);
179 });
180
181 assert!(current_snapshot().is_none());
182 }
183
184 #[test]
185 fn test_readonly_snapshot_enter_restores_previous() {
186 let snapshot1 = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
187 let snapshot2 = ReadonlySnapshot::new(2, SnapshotIdSet::new(), None);
188
189 snapshot1.enter(|| {
190 snapshot2.enter(|| {
191 let current = current_snapshot();
192 assert_eq!(current.unwrap().snapshot_id(), 2);
193 });
194
195 let current = current_snapshot();
196 assert_eq!(current.unwrap().snapshot_id(), 1);
197 });
198 }
199
200 #[test]
201 fn test_readonly_snapshot_nested() {
202 let parent = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
203 let nested = parent.take_nested_snapshot(None);
204
205 assert_eq!(nested.snapshot_id(), 1);
206 }
207
208 #[test]
209 fn test_readonly_snapshot_read_observer() {
210 use std::sync::{Arc as StdArc, Mutex};
211
212 let read_count = StdArc::new(Mutex::new(0));
213 let read_count_clone = read_count.clone();
214
215 let observer = Arc::new(move |_: &dyn StateObject| {
216 *read_count_clone.lock().unwrap() += 1;
217 });
218
219 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), Some(observer));
220 let mock_state = MockStateObject;
221
222 snapshot.record_read(&mock_state);
223 snapshot.record_read(&mock_state);
224
225 assert_eq!(*read_count.lock().unwrap(), 2);
226 }
227
228 #[test]
229 fn test_readonly_snapshot_nested_with_observer() {
230 use std::sync::{Arc as StdArc, Mutex};
231
232 let parent_reads = StdArc::new(Mutex::new(0));
233 let parent_reads_clone = parent_reads.clone();
234 let parent_observer = Arc::new(move |_: &dyn StateObject| {
235 *parent_reads_clone.lock().unwrap() += 1;
236 });
237
238 let nested_reads = StdArc::new(Mutex::new(0));
239 let nested_reads_clone = nested_reads.clone();
240 let nested_observer = Arc::new(move |_: &dyn StateObject| {
241 *nested_reads_clone.lock().unwrap() += 1;
242 });
243
244 let parent = ReadonlySnapshot::new(1, SnapshotIdSet::new(), Some(parent_observer));
245 let nested = parent.take_nested_snapshot(Some(nested_observer));
246
247 let mock_state = MockStateObject;
248
249 nested.record_read(&mock_state);
250
251 assert_eq!(*parent_reads.lock().unwrap(), 1);
252 assert_eq!(*nested_reads.lock().unwrap(), 1);
253 }
254
255 #[test]
256 #[should_panic(expected = "Cannot write to a read-only snapshot")]
257 fn test_readonly_snapshot_write_panics() {
258 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
259 let mock_state = Arc::new(MockStateObject);
260 snapshot.record_write(mock_state);
261 }
262
263 #[test]
264 fn test_readonly_snapshot_dispose() {
265 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
266 assert!(!snapshot.is_disposed());
267
268 snapshot.dispose();
269 assert!(snapshot.is_disposed());
270 }
271}