cranpose_core/snapshot_v2/
readonly.rs1use super::*;
4
5#[allow(clippy::arc_with_non_send_sync)]
15pub struct ReadonlySnapshot {
16 state: SnapshotState,
17}
18
19impl ReadonlySnapshot {
20 pub fn new(
22 id: SnapshotId,
23 invalid: SnapshotIdSet,
24 read_observer: Option<ReadObserver>,
25 ) -> Arc<Self> {
26 Arc::new(Self {
27 state: SnapshotState::new(id, invalid, read_observer, None, false),
28 })
29 }
30
31 pub fn snapshot_id(&self) -> SnapshotId {
32 self.state.id.get()
33 }
34
35 pub fn invalid(&self) -> SnapshotIdSet {
36 self.state.invalid.borrow().clone()
37 }
38
39 pub fn read_only(&self) -> bool {
40 true
41 }
42
43 pub fn root_readonly(&self) -> Arc<Self> {
44 ReadonlySnapshot::new(
46 self.state.id.get(),
47 self.state.invalid.borrow().clone(),
48 self.state.read_observer.clone(),
49 )
50 }
51
52 pub fn enter<T>(&self, f: impl FnOnce() -> T) -> T {
53 let previous = current_snapshot();
54 set_current_snapshot(Some(AnySnapshot::Readonly(self.root_readonly())));
55 let result = f();
56 set_current_snapshot(previous);
57 result
58 }
59
60 pub fn take_nested_snapshot(&self, read_observer: Option<ReadObserver>) -> Arc<Self> {
61 let merged_observer = merge_read_observers(read_observer, self.state.read_observer.clone());
62 ReadonlySnapshot::new(
63 self.state.id.get(),
64 self.state.invalid.borrow().clone(),
65 merged_observer,
66 )
67 }
68
69 pub fn has_pending_changes(&self) -> bool {
70 false }
72
73 pub fn dispose(&self) {
74 self.state.dispose();
75 }
76
77 pub fn record_read(&self, state: &dyn StateObject) {
78 self.state.record_read(state);
79 }
80
81 pub fn record_write(&self, _state: Arc<dyn StateObject>) {
82 panic!("Cannot write to a read-only snapshot");
83 }
84
85 pub fn is_disposed(&self) -> bool {
86 self.state.disposed.get()
87 }
88
89 pub(crate) fn set_on_dispose<F>(&self, f: F)
91 where
92 F: FnOnce() + 'static,
93 {
94 self.state.set_on_dispose(f);
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use crate::state::StateObject;
102 use std::cell::Cell;
103
104 #[allow(dead_code)]
106 struct MockStateObject {
107 value: Cell<i32>,
108 }
109
110 impl StateObject for MockStateObject {
111 fn object_id(&self) -> crate::state::ObjectId {
112 crate::state::ObjectId(0)
113 }
114
115 fn first_record(&self) -> Arc<crate::state::StateRecord> {
116 unimplemented!("Not needed for tests")
117 }
118
119 fn readable_record(
120 &self,
121 _snapshot_id: crate::snapshot_id_set::SnapshotId,
122 _invalid: &SnapshotIdSet,
123 ) -> Arc<crate::state::StateRecord> {
124 unimplemented!("Not needed for tests")
125 }
126
127 fn prepend_state_record(&self, _record: Arc<crate::state::StateRecord>) {
128 unimplemented!("Not needed for tests")
129 }
130
131 fn promote_record(
132 &self,
133 _child_id: crate::snapshot_id_set::SnapshotId,
134 ) -> Result<(), &'static str> {
135 unimplemented!("Not needed for tests")
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)); assert!(!any_snapshot.is_valid(11)); }
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); }
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 value: Cell::new(42),
222 };
223
224 snapshot.record_read(&mock_state);
225 snapshot.record_read(&mock_state);
226
227 assert_eq!(*read_count.lock().unwrap(), 2);
228 }
229
230 #[test]
231 fn test_readonly_snapshot_nested_with_observer() {
232 use std::sync::{Arc as StdArc, Mutex};
233
234 let parent_reads = StdArc::new(Mutex::new(0));
235 let parent_reads_clone = parent_reads.clone();
236 let parent_observer = Arc::new(move |_: &dyn StateObject| {
237 *parent_reads_clone.lock().unwrap() += 1;
238 });
239
240 let nested_reads = StdArc::new(Mutex::new(0));
241 let nested_reads_clone = nested_reads.clone();
242 let nested_observer = Arc::new(move |_: &dyn StateObject| {
243 *nested_reads_clone.lock().unwrap() += 1;
244 });
245
246 let parent = ReadonlySnapshot::new(1, SnapshotIdSet::new(), Some(parent_observer));
247 let nested = parent.take_nested_snapshot(Some(nested_observer));
248
249 let mock_state = MockStateObject {
250 value: Cell::new(42),
251 };
252
253 nested.record_read(&mock_state);
255
256 assert_eq!(*parent_reads.lock().unwrap(), 1);
257 assert_eq!(*nested_reads.lock().unwrap(), 1);
258 }
259
260 #[test]
261 #[should_panic(expected = "Cannot write to a read-only snapshot")]
262 fn test_readonly_snapshot_write_panics() {
263 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
264 let mock_state = Arc::new(MockStateObject {
265 value: Cell::new(42),
266 });
267 snapshot.record_write(mock_state);
268 }
269
270 #[test]
271 fn test_readonly_snapshot_dispose() {
272 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
273 assert!(!snapshot.is_disposed());
274
275 snapshot.dispose();
276 assert!(snapshot.is_disposed());
277 }
278}