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