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, PREEXISTING_SNAPSHOT_ID};
102 use std::rc::Rc;
103
104 fn mock_state_record() -> Rc<crate::state::StateRecord> {
105 crate::state::StateRecord::new(PREEXISTING_SNAPSHOT_ID, (), None)
106 }
107
108 struct MockStateObject;
110
111 impl StateObject for MockStateObject {
112 fn object_id(&self) -> crate::state::ObjectId {
113 crate::state::ObjectId(0)
114 }
115
116 fn first_record(&self) -> Rc<crate::state::StateRecord> {
117 mock_state_record()
118 }
119
120 fn readable_record(
121 &self,
122 _snapshot_id: crate::snapshot_id_set::SnapshotId,
123 _invalid: &SnapshotIdSet,
124 ) -> Rc<crate::state::StateRecord> {
125 mock_state_record()
126 }
127
128 fn prepend_state_record(&self, _record: Rc<crate::state::StateRecord>) {}
129
130 fn promote_record(
131 &self,
132 _child_id: crate::snapshot_id_set::SnapshotId,
133 ) -> Result<(), &'static str> {
134 Ok(())
135 }
136
137 fn as_any(&self) -> &dyn std::any::Any {
138 self
139 }
140 }
141
142 #[test]
143 fn test_readonly_snapshot_creation() {
144 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
145 assert_eq!(snapshot.snapshot_id(), 1);
146 assert!(!snapshot.is_disposed());
147 }
148
149 #[test]
150 fn test_readonly_snapshot_is_valid() {
151 let invalid = SnapshotIdSet::new().set(5);
152 let snapshot = ReadonlySnapshot::new(10, invalid, None);
153
154 let any_snapshot = AnySnapshot::Readonly(snapshot.clone());
155 assert!(any_snapshot.is_valid(1));
156 assert!(any_snapshot.is_valid(10));
157 assert!(!any_snapshot.is_valid(5)); assert!(!any_snapshot.is_valid(11)); }
160
161 #[test]
162 fn test_readonly_snapshot_no_pending_changes() {
163 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
164 assert!(!snapshot.has_pending_changes());
165 }
166
167 #[test]
168 fn test_readonly_snapshot_enter() {
169 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
170
171 set_current_snapshot(None);
172 assert!(current_snapshot().is_none());
173
174 snapshot.enter(|| {
175 let current = current_snapshot();
176 assert!(current.is_some());
177 assert_eq!(current.unwrap().snapshot_id(), 1);
178 });
179
180 assert!(current_snapshot().is_none());
181 }
182
183 #[test]
184 fn test_readonly_snapshot_enter_restores_previous() {
185 let snapshot1 = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
186 let snapshot2 = ReadonlySnapshot::new(2, SnapshotIdSet::new(), None);
187
188 snapshot1.enter(|| {
189 snapshot2.enter(|| {
190 let current = current_snapshot();
191 assert_eq!(current.unwrap().snapshot_id(), 2);
192 });
193
194 let current = current_snapshot();
195 assert_eq!(current.unwrap().snapshot_id(), 1);
196 });
197 }
198
199 #[test]
200 fn test_readonly_snapshot_nested() {
201 let parent = ReadonlySnapshot::new(1, SnapshotIdSet::new(), None);
202 let nested = parent.take_nested_snapshot(None);
203
204 assert_eq!(nested.snapshot_id(), 1); }
206
207 #[test]
208 fn test_readonly_snapshot_read_observer() {
209 use std::sync::{Arc as StdArc, Mutex};
210
211 let read_count = StdArc::new(Mutex::new(0));
212 let read_count_clone = read_count.clone();
213
214 let observer = Arc::new(move |_: &dyn StateObject| {
215 *read_count_clone.lock().unwrap() += 1;
216 });
217
218 let snapshot = ReadonlySnapshot::new(1, SnapshotIdSet::new(), Some(observer));
219 let mock_state = MockStateObject;
220
221 snapshot.record_read(&mock_state);
222 snapshot.record_read(&mock_state);
223
224 assert_eq!(*read_count.lock().unwrap(), 2);
225 }
226
227 #[test]
228 fn test_readonly_snapshot_nested_with_observer() {
229 use std::sync::{Arc as StdArc, Mutex};
230
231 let parent_reads = StdArc::new(Mutex::new(0));
232 let parent_reads_clone = parent_reads.clone();
233 let parent_observer = Arc::new(move |_: &dyn StateObject| {
234 *parent_reads_clone.lock().unwrap() += 1;
235 });
236
237 let nested_reads = StdArc::new(Mutex::new(0));
238 let nested_reads_clone = nested_reads.clone();
239 let nested_observer = Arc::new(move |_: &dyn StateObject| {
240 *nested_reads_clone.lock().unwrap() += 1;
241 });
242
243 let parent = ReadonlySnapshot::new(1, SnapshotIdSet::new(), Some(parent_observer));
244 let nested = parent.take_nested_snapshot(Some(nested_observer));
245
246 let mock_state = MockStateObject;
247
248 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}