cranpose_core/snapshot_v2/
mutable.rs1use std::{rc::Rc, sync::Arc};
2
3use super::*;
4use crate::{
5 collections::map::HashMap,
6 state::{PREEXISTING_SNAPSHOT_ID, StateRecord},
7};
8
9pub(super) fn find_record_by_id(
10 head: &Rc<StateRecord>,
11 target: SnapshotId,
12) -> Option<Rc<StateRecord>> {
13 let mut cursor = Some(Rc::clone(head));
14 while let Some(record) = cursor {
15 if !record.is_tombstone() && record.snapshot_id() == target {
16 return Some(record);
17 }
18 cursor = record.next();
19 }
20 None
21}
22
23pub(super) fn find_previous_record(
24 head: &Rc<StateRecord>,
25 base_snapshot_id: SnapshotId,
26 invalid: &SnapshotIdSet,
27) -> (Option<Rc<StateRecord>>, bool) {
28 let mut cursor = Some(Rc::clone(head));
29 let mut best: Option<Rc<StateRecord>> = None;
30 let mut fallback: Option<Rc<StateRecord>> = None;
31 let mut found_base = false;
32
33 while let Some(record) = cursor {
34 if !record.is_tombstone() {
35 let id = record.snapshot_id();
36 let is_valid = id <= base_snapshot_id && !invalid.get(id);
37 if is_valid {
38 found_base = true;
39 let replace = best
40 .as_ref()
41 .is_none_or(|current| current.snapshot_id() < id);
42 if replace {
43 best = Some(record.clone());
44 }
45 }
46 if fallback.is_none() {
47 fallback = Some(record.clone());
48 }
49 }
50 cursor = record.next();
51 }
52
53 (best.or(fallback), found_base)
54}
55
56enum ApplyOperation {
57 PromoteChild {
58 object_id: StateObjectId,
59 state: Arc<dyn StateObject>,
60 writer_id: SnapshotId,
61 },
62 PromoteExisting {
63 object_id: StateObjectId,
64 state: Arc<dyn StateObject>,
65 source_id: SnapshotId,
66 applied: Rc<StateRecord>,
67 },
68 CommitMerged {
69 object_id: StateObjectId,
70 state: Arc<dyn StateObject>,
71 merged: Rc<StateRecord>,
72 applied: Rc<StateRecord>,
73 },
74}
75
76pub struct MutableSnapshot {
87 state: SnapshotState,
88 base_parent_id: SnapshotId,
89 nested_count: Cell<usize>,
90 applied: Cell<bool>,
91}
92
93impl MutableSnapshot {
94 pub(crate) fn from_parts(
95 id: SnapshotId,
96 invalid: SnapshotIdSet,
97 read_observer: Option<ReadObserver>,
98 write_observer: Option<WriteObserver>,
99 base_parent_id: SnapshotId,
100 runtime_tracked: bool,
101 ) -> Arc<Self> {
102 Arc::new(Self {
103 state: SnapshotState::new(id, invalid, read_observer, write_observer, runtime_tracked),
104 base_parent_id,
105 nested_count: Cell::new(0),
106 applied: Cell::new(false),
107 })
108 }
109
110 pub fn new(
112 id: SnapshotId,
113 invalid: SnapshotIdSet,
114 read_observer: Option<ReadObserver>,
115 write_observer: Option<WriteObserver>,
116 base_parent_id: SnapshotId,
117 ) -> Arc<Self> {
118 Self::from_parts(
119 id,
120 invalid,
121 read_observer,
122 write_observer,
123 base_parent_id,
124 false,
125 )
126 }
127
128 fn validate_not_applied(&self) {
129 assert!(!self.applied.get(), "Snapshot has already been applied");
130 }
131
132 fn validate_not_disposed(&self) {
133 assert!(!self.state.disposed.get(), "Snapshot has been disposed");
134 }
135
136 pub fn snapshot_id(&self) -> SnapshotId {
137 self.state.id.get()
138 }
139
140 pub fn invalid(&self) -> SnapshotIdSet {
141 self.state.invalid.borrow().clone()
142 }
143
144 pub fn read_only(&self) -> bool {
145 false
146 }
147
148 pub(crate) fn set_on_dispose<F>(&self, f: F)
149 where
150 F: FnOnce() + 'static,
151 {
152 self.state.set_on_dispose(f);
153 }
154
155 pub fn root_mutable(self: &Arc<Self>) -> Arc<Self> {
156 self.clone()
157 }
158
159 pub fn enter<T>(self: &Arc<Self>, f: impl FnOnce() -> T) -> T {
160 enter_snapshot_scope(AnySnapshot::Mutable(self.clone()), f)
161 }
162
163 pub fn take_nested_snapshot(
164 self: &Arc<Self>,
165 read_observer: Option<ReadObserver>,
166 ) -> Arc<ReadonlySnapshot> {
167 self.validate_not_disposed();
168 self.validate_not_applied();
169
170 let merged_observer =
171 merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
172
173 let nested = ReadonlySnapshot::new(
174 self.state.id.get(),
175 self.state.invalid.borrow().clone(),
176 merged_observer,
177 );
178
179 self.nested_count.set(self.nested_count.get() + 1);
180
181 let parent_weak = Arc::downgrade(self);
182 nested.set_on_dispose(move || {
183 if let Some(parent) = parent_weak.upgrade() {
184 let cur = parent.nested_count.get();
185 if cur > 0 {
186 parent.nested_count.set(cur - 1);
187 }
188 }
189 });
190 nested
191 }
192
193 pub fn has_pending_changes(&self) -> bool {
194 !self.state.modified.borrow().is_empty()
195 }
196
197 pub fn pending_children(&self) -> Vec<SnapshotId> {
198 self.state.pending_children()
199 }
200
201 pub fn has_pending_children(&self) -> bool {
202 self.state.has_pending_children()
203 }
204
205 pub fn dispose(&self) {
206 if !self.state.disposed.get() && self.nested_count.get() == 0 {
207 self.state.dispose();
208 }
209 }
210
211 pub fn record_read(&self, state: &dyn StateObject) {
212 self.state.record_read(state);
213 }
214
215 pub fn record_write(&self, state: Arc<dyn StateObject>) {
216 self.validate_not_applied();
217 self.validate_not_disposed();
218 self.state.record_write(state, self.state.id.get());
219 }
220
221 pub fn close(&self) {
222 self.state.disposed.set(true);
223 }
224
225 pub fn is_disposed(&self) -> bool {
226 self.state.disposed.get()
227 }
228
229 pub fn apply(&self) -> SnapshotApplyResult {
230 if self.state.disposed.get() {
231 return SnapshotApplyResult::Failure;
232 }
233
234 if self.applied.get() {
235 return SnapshotApplyResult::Failure;
236 }
237
238 let modified = self.state.modified.borrow();
239 if modified.is_empty() {
240 self.applied.set(true);
241 self.state.dispose();
242 return SnapshotApplyResult::Success;
243 }
244
245 let this_id = self.state.id.get();
246 let mut modified_objects: Vec<(StateObjectId, Arc<dyn StateObject>, SnapshotId)> =
247 Vec::with_capacity(modified.len());
248 for (&obj_id, (obj, writer_id)) in modified.iter() {
249 modified_objects.push((obj_id, obj.clone(), *writer_id));
250 }
251
252 drop(modified);
253
254 let parent_snapshot = GlobalSnapshot::get_or_create();
255 let parent_snapshot_id = parent_snapshot.snapshot_id();
256 let parent_invalid = parent_snapshot.invalid();
257 drop(parent_snapshot);
258
259 let next_invalid = super::runtime::open_snapshots().clear(parent_snapshot_id);
260 let this_invalid_for_optimistic = self.state.invalid.borrow().clone();
261 let optimistic = super::optimistic_merges(
262 parent_snapshot_id,
263 self.base_parent_id,
264 &modified_objects,
265 &next_invalid,
266 &this_invalid_for_optimistic,
267 );
268
269 let mut operations: Vec<ApplyOperation> = Vec::with_capacity(modified_objects.len());
270
271 for (obj_id, state, writer_id) in &modified_objects {
272 let head = state.first_record();
273 let Some(applied) = find_record_by_id(&head, *writer_id) else {
274 return SnapshotApplyResult::Failure;
275 };
276
277 let Some(current) =
278 crate::state::readable_record_for(&head, parent_snapshot_id, &next_invalid)
279 .or_else(|| state.try_readable_record(parent_snapshot_id, &parent_invalid))
280 else {
281 log::error!(
282 "MutableSnapshot::apply missing parent readable record (object_id={obj_id:?}, parent_snapshot_id={parent_snapshot_id})"
283 );
284 return SnapshotApplyResult::Failure;
285 };
286 let this_invalid = self.state.invalid.borrow();
287 let (previous_opt, found_base) =
288 find_previous_record(&head, self.base_parent_id, &this_invalid);
289 drop(this_invalid);
290 let Some(previous) = previous_opt else {
291 return SnapshotApplyResult::Failure;
292 };
293
294 if !found_base || previous.snapshot_id() == PREEXISTING_SNAPSHOT_ID {
295 operations.push(ApplyOperation::PromoteChild {
296 object_id: *obj_id,
297 state: state.clone(),
298 writer_id: *writer_id,
299 });
300 continue;
301 }
302
303 if Rc::ptr_eq(¤t, &previous) {
304 operations.push(ApplyOperation::PromoteChild {
305 object_id: *obj_id,
306 state: state.clone(),
307 writer_id: *writer_id,
308 });
309 continue;
310 }
311
312 let merged = if let Some(candidate) = optimistic
313 .as_ref()
314 .and_then(|map| map.get(&(Rc::as_ptr(¤t) as usize)))
315 .cloned()
316 {
317 candidate
318 } else {
319 match state.merge_records(
320 Rc::clone(&previous),
321 Rc::clone(¤t),
322 Rc::clone(&applied),
323 ) {
324 Some(record) => record,
325 None => return SnapshotApplyResult::Failure,
326 }
327 };
328
329 if Rc::ptr_eq(&merged, &applied) {
330 operations.push(ApplyOperation::PromoteChild {
331 object_id: *obj_id,
332 state: state.clone(),
333 writer_id: *writer_id,
334 });
335 } else if Rc::ptr_eq(&merged, ¤t) {
336 operations.push(ApplyOperation::PromoteExisting {
337 object_id: *obj_id,
338 state: state.clone(),
339 source_id: current.snapshot_id(),
340 applied: applied.clone(),
341 });
342 } else {
343 operations.push(ApplyOperation::CommitMerged {
344 object_id: *obj_id,
345 state: state.clone(),
346 merged: merged.clone(),
347 applied: applied.clone(),
348 });
349 }
350 }
351
352 let mut applied_info: Vec<(StateObjectId, Arc<dyn StateObject>, SnapshotId)> =
353 Vec::with_capacity(operations.len());
354
355 for operation in operations {
356 match operation {
357 ApplyOperation::PromoteChild {
358 object_id,
359 state,
360 writer_id,
361 } => {
362 if state.promote_record(writer_id).is_err() {
363 return SnapshotApplyResult::Failure;
364 }
365 let new_head_id = state.first_record().snapshot_id();
366 applied_info.push((object_id, state, new_head_id));
367 }
368 ApplyOperation::PromoteExisting {
369 object_id,
370 state,
371 source_id,
372 applied,
373 } => {
374 if state.promote_record(source_id).is_err() {
375 return SnapshotApplyResult::Failure;
376 }
377 applied.set_tombstone(true);
378 applied.clear_value();
379 let new_head_id = state.first_record().snapshot_id();
380 applied_info.push((object_id, state, new_head_id));
381 }
382 ApplyOperation::CommitMerged {
383 object_id,
384 state,
385 merged,
386 applied,
387 } => {
388 let Ok(new_head_id) = state.commit_merged_record(merged) else {
389 return SnapshotApplyResult::Failure;
390 };
391 applied.set_tombstone(true);
392 applied.clear_value();
393 applied_info.push((object_id, state, new_head_id));
394 }
395 }
396 }
397
398 for (obj_id, _, head_id) in &applied_info {
399 super::set_last_write(*obj_id, *head_id);
400 }
401
402 self.applied.set(true);
403 self.state.dispose();
404
405 for (_, state, _) in &applied_info {
406 super::EXTRA_STATE_OBJECTS.with(|cell| {
407 cell.borrow_mut().add_trait_object(state);
408 });
409 }
410
411 let observer_states: Vec<Arc<dyn StateObject>> = applied_info
412 .iter()
413 .map(|(_, state, _)| state.clone())
414 .collect();
415 super::notify_apply_observers(&observer_states, this_id);
416 SnapshotApplyResult::Success
417 }
418
419 pub fn take_nested_mutable_snapshot(
420 self: &Arc<Self>,
421 read_observer: Option<ReadObserver>,
422 write_observer: Option<WriteObserver>,
423 ) -> Arc<NestedMutableSnapshot> {
424 self.validate_not_disposed();
425 self.validate_not_applied();
426
427 allocate_nested_mutable_snapshot(self, Arc::downgrade(self), read_observer, write_observer)
428 }
429
430 pub(crate) fn merge_child_modifications(
431 &self,
432 child_modified: &HashMap<StateObjectId, (Arc<dyn StateObject>, SnapshotId)>,
433 ) -> Result<(), ()> {
434 {
435 let parent_mod = self.state.modified.borrow();
436 for key in child_modified.keys() {
437 if parent_mod.contains_key(key) {
438 return Err(());
439 }
440 }
441 }
442
443 let mut parent_mod = self.state.modified.borrow_mut();
444 for (key, value) in child_modified {
445 parent_mod.entry(*key).or_insert_with(|| value.clone());
446 }
447 Ok(())
448 }
449}
450
451impl NestedMutableHost for MutableSnapshot {
452 fn snapshot_state(&self) -> &SnapshotState {
453 &self.state
454 }
455
456 fn nested_count(&self) -> &Cell<usize> {
457 &self.nested_count
458 }
459}
460
461#[cfg(test)]
462impl MutableSnapshot {
463 pub(crate) fn debug_modified_objects(
464 &self,
465 ) -> Vec<(StateObjectId, Arc<dyn StateObject>, SnapshotId)> {
466 let modified = self.state.modified.borrow();
467 modified
468 .iter()
469 .map(|(&obj_id, (state, writer_id))| (obj_id, state.clone(), *writer_id))
470 .collect()
471 }
472
473 pub(crate) fn debug_base_parent_id(&self) -> SnapshotId {
474 self.base_parent_id
475 }
476}
477
478#[cfg(test)]
479#[path = "tests/mutable_tests.rs"]
480mod tests;