cranpose_core/snapshot_v2/
mutable.rs1use std::rc::Rc;
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: Rc<dyn StateObject>,
60 writer_id: SnapshotId,
61 },
62 PromoteExisting {
63 object_id: StateObjectId,
64 state: Rc<dyn StateObject>,
65 source_id: SnapshotId,
66 applied: Rc<StateRecord>,
67 },
68 CommitMerged {
69 object_id: StateObjectId,
70 state: Rc<dyn StateObject>,
71 merged: Rc<StateRecord>,
72 applied: Rc<StateRecord>,
73 },
74}
75
76pub struct MutableSnapshot {
87 pub(super) 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 ) -> Rc<Self> {
102 Rc::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 ) -> Rc<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 {
138 self.state.id.get()
139 }
140
141 pub fn invalid(&self) -> SnapshotIdSet {
143 self.state.invalid.borrow().clone()
144 }
145
146 pub fn read_only(&self) -> bool {
148 false
149 }
150
151 pub(crate) fn set_on_dispose<F>(&self, f: F)
152 where
153 F: FnOnce() + 'static,
154 {
155 self.state.set_on_dispose(f);
156 }
157
158 pub fn root_mutable(self: &Rc<Self>) -> Rc<Self> {
160 self.clone()
161 }
162
163 pub fn enter<T>(self: &Rc<Self>, f: impl FnOnce() -> T) -> T {
166 enter_snapshot_scope(AnySnapshot::Mutable(self.clone()), f)
167 }
168
169 pub fn take_nested_snapshot(
173 self: &Rc<Self>,
174 read_observer: Option<ReadObserver>,
175 ) -> Rc<ReadonlySnapshot> {
176 self.validate_not_disposed();
177 self.validate_not_applied();
178
179 let merged_observer =
180 merge_read_observers(read_observer, self.state.read_observer.borrow().clone());
181
182 let nested = ReadonlySnapshot::new(
183 self.state.id.get(),
184 self.state.invalid.borrow().clone(),
185 merged_observer,
186 );
187
188 self.nested_count.set(self.nested_count.get() + 1);
189
190 let parent_weak = Rc::downgrade(self);
191 nested.set_on_dispose(move || {
192 if let Some(parent) = parent_weak.upgrade() {
193 let cur = parent.nested_count.get();
194 if cur > 0 {
195 parent.nested_count.set(cur - 1);
196 }
197 }
198 });
199 nested
200 }
201
202 pub fn has_pending_changes(&self) -> bool {
204 !self.state.modified.borrow().is_empty()
205 }
206
207 pub fn pending_children(&self) -> Vec<SnapshotId> {
209 self.state.pending_children()
210 }
211
212 pub fn has_pending_children(&self) -> bool {
214 self.state.has_pending_children()
215 }
216
217 pub fn dispose(&self) {
220 if !self.state.disposed.get() && self.nested_count.get() == 0 {
221 self.state.dispose();
222 }
223 }
224
225 pub fn record_read(&self, state: &dyn StateObject) {
227 self.state.record_read(state);
228 }
229
230 pub fn record_write(&self, state: Rc<dyn StateObject>) {
233 self.validate_not_applied();
234 self.validate_not_disposed();
235 self.state.record_write(state, self.state.id.get());
236 }
237
238 pub fn is_disposed(&self) -> bool {
240 self.state.disposed.get()
241 }
242
243 pub fn apply(&self) -> SnapshotApplyResult {
248 let _retiring = crate::state::RetiredValuesScope::enter();
250 if self.state.disposed.get() {
251 return SnapshotApplyResult::Failure;
252 }
253
254 if self.applied.get() {
255 return SnapshotApplyResult::Failure;
256 }
257
258 let modified = self.state.modified.borrow();
259 if modified.is_empty() {
260 self.applied.set(true);
261 self.state.dispose();
262 return SnapshotApplyResult::Success;
263 }
264
265 let this_id = self.state.id.get();
266 let mut modified_objects: Vec<(StateObjectId, Rc<dyn StateObject>, SnapshotId)> =
267 Vec::with_capacity(modified.len());
268 for (&obj_id, (obj, writer_id)) in modified.iter() {
269 modified_objects.push((obj_id, obj.clone(), *writer_id));
270 }
271
272 drop(modified);
273
274 let parent_snapshot = GlobalSnapshot::get_or_create();
275 let parent_snapshot_id = parent_snapshot.snapshot_id();
276 let parent_invalid = parent_snapshot.invalid();
277 drop(parent_snapshot);
278
279 let next_invalid = super::runtime::open_snapshots().clear(parent_snapshot_id);
280 let this_invalid_for_optimistic = self.state.invalid.borrow().clone();
281 let optimistic = super::optimistic_merges(
282 parent_snapshot_id,
283 self.base_parent_id,
284 &modified_objects,
285 &next_invalid,
286 &this_invalid_for_optimistic,
287 );
288
289 let mut operations: Vec<ApplyOperation> = Vec::with_capacity(modified_objects.len());
290
291 for (obj_id, state, writer_id) in &modified_objects {
292 let head = state.first_record();
293 let Some(applied) = find_record_by_id(&head, *writer_id) else {
294 return SnapshotApplyResult::Failure;
295 };
296
297 let Some(current) =
298 crate::state::readable_record_for(&head, parent_snapshot_id, &next_invalid)
299 .or_else(|| state.try_readable_record(parent_snapshot_id, &parent_invalid))
300 else {
301 log::error!(
302 "MutableSnapshot::apply missing parent readable record (object_id={obj_id:?}, parent_snapshot_id={parent_snapshot_id})"
303 );
304 return SnapshotApplyResult::Failure;
305 };
306 let this_invalid = self.state.invalid.borrow();
307 let (previous_opt, found_base) =
308 find_previous_record(&head, self.base_parent_id, &this_invalid);
309 drop(this_invalid);
310 let Some(previous) = previous_opt else {
311 return SnapshotApplyResult::Failure;
312 };
313
314 if !found_base || previous.snapshot_id() == PREEXISTING_SNAPSHOT_ID {
315 operations.push(ApplyOperation::PromoteChild {
316 object_id: *obj_id,
317 state: state.clone(),
318 writer_id: *writer_id,
319 });
320 continue;
321 }
322
323 if Rc::ptr_eq(¤t, &previous) {
324 operations.push(ApplyOperation::PromoteChild {
325 object_id: *obj_id,
326 state: state.clone(),
327 writer_id: *writer_id,
328 });
329 continue;
330 }
331
332 let merged = if let Some(candidate) = optimistic
333 .as_ref()
334 .and_then(|map| map.get(&(Rc::as_ptr(¤t) as usize)))
335 .cloned()
336 {
337 candidate
338 } else {
339 match state.merge_records(
340 Rc::clone(&previous),
341 Rc::clone(¤t),
342 Rc::clone(&applied),
343 ) {
344 Some(record) => record,
345 None => return SnapshotApplyResult::Failure,
346 }
347 };
348
349 if Rc::ptr_eq(&merged, &applied) {
350 operations.push(ApplyOperation::PromoteChild {
351 object_id: *obj_id,
352 state: state.clone(),
353 writer_id: *writer_id,
354 });
355 } else if Rc::ptr_eq(&merged, ¤t) {
356 operations.push(ApplyOperation::PromoteExisting {
357 object_id: *obj_id,
358 state: state.clone(),
359 source_id: current.snapshot_id(),
360 applied: applied.clone(),
361 });
362 } else {
363 operations.push(ApplyOperation::CommitMerged {
364 object_id: *obj_id,
365 state: state.clone(),
366 merged: merged.clone(),
367 applied: applied.clone(),
368 });
369 }
370 }
371
372 let mut applied_info: Vec<(StateObjectId, Rc<dyn StateObject>, SnapshotId)> =
373 Vec::with_capacity(operations.len());
374
375 for operation in operations {
376 match operation {
377 ApplyOperation::PromoteChild {
378 object_id,
379 state,
380 writer_id,
381 } => {
382 if state.promote_record(writer_id).is_err() {
383 return SnapshotApplyResult::Failure;
384 }
385 let new_head_id = state.first_record().snapshot_id();
386 applied_info.push((object_id, state, new_head_id));
387 }
388 ApplyOperation::PromoteExisting {
389 object_id,
390 state,
391 source_id,
392 applied,
393 } => {
394 if state.promote_record(source_id).is_err() {
395 return SnapshotApplyResult::Failure;
396 }
397 applied.tombstone_retiring_value();
398 let new_head_id = state.first_record().snapshot_id();
399 applied_info.push((object_id, state, new_head_id));
400 }
401 ApplyOperation::CommitMerged {
402 object_id,
403 state,
404 merged,
405 applied,
406 } => {
407 let Ok(new_head_id) = state.commit_merged_record(merged) else {
408 return SnapshotApplyResult::Failure;
409 };
410 applied.tombstone_retiring_value();
411 applied_info.push((object_id, state, new_head_id));
412 }
413 }
414 }
415
416 for (obj_id, _, head_id) in &applied_info {
417 super::set_last_write(*obj_id, *head_id);
418 }
419
420 self.applied.set(true);
421 self.state.dispose();
422
423 crate::thread_state::snapshots(|snapshots| {
424 let mut extra_state_objects = snapshots.extra_state_objects.borrow_mut();
425 for (_, state, _) in &applied_info {
426 extra_state_objects.add_trait_object(state);
427 }
428 });
429
430 let observer_states: Vec<Rc<dyn StateObject>> = applied_info
431 .iter()
432 .map(|(_, state, _)| state.clone())
433 .collect();
434 super::notify_apply_observers(&observer_states, this_id);
435 SnapshotApplyResult::Success
436 }
437
438 pub fn take_nested_mutable_snapshot(
441 self: &Rc<Self>,
442 read_observer: Option<ReadObserver>,
443 write_observer: Option<WriteObserver>,
444 ) -> Rc<NestedMutableSnapshot> {
445 self.validate_not_disposed();
446 self.validate_not_applied();
447
448 allocate_nested_mutable_snapshot(self, Rc::downgrade(self), read_observer, write_observer)
449 }
450
451 pub(crate) fn merge_child_modifications(
452 &self,
453 child_modified: &HashMap<StateObjectId, (Rc<dyn StateObject>, SnapshotId)>,
454 ) -> Result<(), ()> {
455 {
456 let parent_mod = self.state.modified.borrow();
457 for key in child_modified.keys() {
458 if parent_mod.contains_key(key) {
459 return Err(());
460 }
461 }
462 }
463
464 let mut parent_mod = self.state.modified.borrow_mut();
465 for (key, value) in child_modified {
466 parent_mod.entry(*key).or_insert_with(|| value.clone());
467 }
468 Ok(())
469 }
470}
471
472impl NestedMutableHost for MutableSnapshot {
473 fn snapshot_state(&self) -> &SnapshotState {
474 &self.state
475 }
476
477 fn nested_count(&self) -> &Cell<usize> {
478 &self.nested_count
479 }
480}
481
482#[cfg(test)]
483impl MutableSnapshot {
484 pub(crate) fn debug_modified_objects(
485 &self,
486 ) -> Vec<(StateObjectId, Rc<dyn StateObject>, SnapshotId)> {
487 let modified = self.state.modified.borrow();
488 modified
489 .iter()
490 .map(|(&obj_id, (state, writer_id))| (obj_id, state.clone(), *writer_id))
491 .collect()
492 }
493
494 pub(crate) fn debug_base_parent_id(&self) -> SnapshotId {
495 self.base_parent_id
496 }
497}
498
499#[cfg(test)]
500#[path = "tests/mutable_tests.rs"]
501mod tests;