1#![allow(clippy::arc_with_non_send_sync)]
3
4use crate::collections::map::{HashMap, HashSet};
5use crate::debug_trace::debug_record_scope_invalidation;
6use std::any::Any;
7use std::cell::{Cell, RefCell};
8use std::fmt;
9use std::hash::Hash;
10use std::marker::PhantomData;
11use std::ops::Deref;
12use std::rc::{Rc, Weak as RcWeak};
13use std::sync::{Arc, Mutex, MutexGuard, Weak};
14
15use crate::snapshot_id_set::{SnapshotId, SnapshotIdSet};
16use crate::snapshot_pinning::lowest_pinned_snapshot;
17use crate::snapshot_v2::{
18 advance_global_snapshot, allocate_record_id, current_snapshot, AnySnapshot, GlobalSnapshot,
19};
20use crate::{runtime, with_current_composer_opt, RecomposeScope, RuntimeHandle, ScopeId, StateId};
21
22pub(crate) const PREEXISTING_SNAPSHOT_ID: SnapshotId = 1;
23
24const INVALID_SNAPSHOT_ID: SnapshotId = 0;
25
26const SNAPSHOT_ID_MAX: SnapshotId = usize::MAX;
28
29#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, Default)]
30pub struct ObjectId(pub(crate) usize);
31
32impl ObjectId {
33 pub(crate) fn new<T: ?Sized + 'static>(object: &Arc<T>) -> Self {
34 Self(Arc::as_ptr(object) as *const () as usize)
35 }
36
37 #[inline]
38 pub(crate) fn as_usize(self) -> usize {
39 self.0
40 }
41}
42
43pub struct StateRecord {
50 snapshot_id: Cell<SnapshotId>,
51 tombstone: Cell<bool>,
52 next: Cell<Option<Rc<StateRecord>>>,
53 value: RefCell<Option<Box<dyn Any>>>,
54}
55
56#[derive(Debug)]
57struct StateReadFailure {
58 state_id: ObjectId,
59 snapshot_id: SnapshotId,
60 fresh_snapshot_id: SnapshotId,
61 fresh_invalid: SnapshotIdSet,
62 record_chain: Vec<(SnapshotId, bool)>,
63}
64
65impl std::fmt::Display for StateReadFailure {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 write!(
68 f,
69 "Reading a state that was created after the snapshot was taken or in a snapshot that has not yet been applied\n\
70 state={:?}, snapshot_id={}, fresh_snapshot_id={}, fresh_invalid={:?}\n\
71 record_chain={:?}",
72 self.state_id,
73 self.snapshot_id,
74 self.fresh_snapshot_id,
75 self.fresh_invalid,
76 self.record_chain
77 )
78 }
79}
80
81#[derive(Debug, Clone, Copy, Eq, PartialEq)]
82pub(crate) enum StateRecordValueError {
83 MissingOrWrongType { expected: &'static str },
84}
85
86impl StateRecord {
87 pub(crate) fn new<T: Any>(
88 snapshot_id: SnapshotId,
89 value: T,
90 next: Option<Rc<StateRecord>>,
91 ) -> Rc<Self> {
92 Rc::new(Self {
93 snapshot_id: Cell::new(snapshot_id),
94 tombstone: Cell::new(false),
95 next: Cell::new(next),
96 value: RefCell::new(Some(Box::new(value))),
97 })
98 }
99
100 #[inline]
101 pub(crate) fn snapshot_id(&self) -> SnapshotId {
102 self.snapshot_id.get()
103 }
104
105 #[inline]
106 pub(crate) fn set_snapshot_id(&self, id: SnapshotId) {
107 self.snapshot_id.set(id);
108 }
109
110 #[inline]
111 pub(crate) fn next(&self) -> Option<Rc<StateRecord>> {
112 self.next.take().inspect(|record| {
113 self.next.set(Some(Rc::clone(record)));
114 })
115 }
116
117 #[inline]
118 pub(crate) fn set_next(&self, next: Option<Rc<StateRecord>>) {
119 self.next.set(next);
120 }
121
122 #[inline]
123 pub(crate) fn is_tombstone(&self) -> bool {
124 self.tombstone.get()
125 }
126
127 #[inline]
128 pub(crate) fn set_tombstone(&self, tombstone: bool) {
129 self.tombstone.set(tombstone);
130 }
131
132 pub(crate) fn clear_value(&self) {
133 self.value.borrow_mut().take();
134 }
135
136 pub(crate) fn replace_value<T: Any>(&self, new_value: T) {
137 *self.value.borrow_mut() = Some(Box::new(new_value));
138 }
139
140 pub(crate) fn with_value<T: Any, R>(&self, f: impl FnOnce(&T) -> R) -> R {
141 self.try_with_value(f)
142 .unwrap_or_else(|| panic!("StateRecord value missing or wrong type"))
143 }
144
145 pub(crate) fn try_with_value<T: Any, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
146 let guard = self.value.borrow();
147 let value = guard.as_ref().and_then(|boxed| boxed.downcast_ref::<T>())?;
148 Some(f(value))
149 }
150
151 #[cfg(test)]
154 pub(crate) fn clear_for_reuse(&self) {
155 self.clear_value();
156 }
157
158 pub(crate) fn assign_value<T: Any + Clone>(
165 &self,
166 source: &StateRecord,
167 ) -> Result<(), StateRecordValueError> {
168 let cloned_value = source.try_with_value(|value: &T| value.clone()).ok_or(
169 StateRecordValueError::MissingOrWrongType {
170 expected: std::any::type_name::<T>(),
171 },
172 )?;
173 self.replace_value(cloned_value);
174 Ok(())
175 }
176}
177
178impl Drop for StateRecord {
179 fn drop(&mut self) {
180 let mut next = self.next.take();
183 while let Some(node) = next {
184 match Rc::try_unwrap(node) {
185 Ok(record) => {
186 next = record.next.take();
190 }
191 Err(_) => {
192 break;
195 }
196 }
197 }
198 }
199}
200
201struct CurrentRecord {
208 head: RefCell<Rc<StateRecord>>,
209}
210
211impl CurrentRecord {
212 fn new(head: Rc<StateRecord>) -> Self {
213 Self {
214 head: RefCell::new(head),
215 }
216 }
217
218 fn clone_head(&self) -> Rc<StateRecord> {
219 self.head.borrow().clone()
220 }
221
222 fn replace(&self, new_head: Rc<StateRecord>) {
223 *self.head.borrow_mut() = new_head;
224 }
225
226 fn prepend(&self, record: Rc<StateRecord>) {
227 let current_head = self.clone_head();
228 record.set_next(Some(current_head));
229 self.replace(record);
230 }
231}
232
233#[inline]
234fn record_is_valid_for(
235 record: &Rc<StateRecord>,
236 snapshot_id: SnapshotId,
237 invalid: &SnapshotIdSet,
238) -> bool {
239 if record.is_tombstone() {
240 return false;
241 }
242
243 let candidate = record.snapshot_id();
244 if candidate == INVALID_SNAPSHOT_ID || candidate > snapshot_id {
245 return false;
246 }
247
248 candidate == snapshot_id || !invalid.get(candidate)
249}
250
251pub(crate) fn readable_record_for(
252 head: &Rc<StateRecord>,
253 snapshot_id: SnapshotId,
254 invalid: &SnapshotIdSet,
255) -> Option<Rc<StateRecord>> {
256 let mut best: Option<Rc<StateRecord>> = None;
260 let mut cursor = Some(Rc::clone(head));
261
262 while let Some(record) = cursor {
263 if record_is_valid_for(&record, snapshot_id, invalid) {
264 let replace = best
265 .as_ref()
266 .map(|current| current.snapshot_id() < record.snapshot_id())
267 .unwrap_or(true);
268 if replace {
269 best = Some(Rc::clone(&record));
270 }
271 }
272 cursor = record.next();
273 }
274
275 best
276}
277
278fn find_youngest_or<F>(head: &Rc<StateRecord>, predicate: F) -> Rc<StateRecord>
284where
285 F: Fn(&Rc<StateRecord>) -> bool,
286{
287 let mut current = Some(Rc::clone(head));
288 let mut youngest = Rc::clone(head);
289
290 while let Some(record) = current {
291 if predicate(&record) {
292 return record;
293 }
294 if youngest.snapshot_id() < record.snapshot_id() {
295 youngest = Rc::clone(&record);
296 }
297 current = record.next();
298 }
299
300 youngest
301}
302
303pub(crate) fn used_locked(head: &Rc<StateRecord>) -> Option<Rc<StateRecord>> {
315 let mut current = Some(Rc::clone(head));
316 let mut valid_record: Option<Rc<StateRecord>> = None;
317
318 let reuse_limit = lowest_pinned_snapshot()
320 .map(|lowest| lowest.saturating_sub(1))
321 .unwrap_or_else(|| allocate_record_id().saturating_sub(1));
322
323 let invalid = SnapshotIdSet::EMPTY;
324
325 while let Some(record) = current {
326 let current_id = record.snapshot_id();
327
328 if current_id == PREEXISTING_SNAPSHOT_ID {
330 current = record.next();
331 continue;
332 }
333
334 if current_id == INVALID_SNAPSHOT_ID {
336 return Some(record);
337 }
338
339 if record.is_tombstone() && current_id < reuse_limit {
340 return Some(record);
341 }
342
343 if record_is_valid_for(&record, reuse_limit, &invalid) {
345 if let Some(ref existing) = valid_record {
346 return Some(if current_id < existing.snapshot_id() {
349 record
350 } else {
351 Rc::clone(existing)
352 });
353 } else {
354 valid_record = Some(record.clone());
356 }
357 }
358
359 current = record.next();
360 }
361
362 None
364}
365
366pub(crate) fn new_overwritable_record_locked(state: &dyn StateObject) -> Rc<StateRecord> {
377 let state_head = state.first_record();
378
379 if let Some(reusable) = used_locked(&state_head) {
381 reusable.set_snapshot_id(SNAPSHOT_ID_MAX);
383 return reusable;
384 }
385
386 let new_record = StateRecord::new(
389 SNAPSHOT_ID_MAX,
390 (),
391 None, );
393
394 state.prepend_state_record(Rc::clone(&new_record));
396
397 new_record
398}
399
400pub(crate) fn new_overwritable_record_as_head_locked(state: &dyn StateObject) -> Rc<StateRecord> {
406 let head = state.first_record();
407
408 if let Some(reusable) = used_locked(&head) {
409 reusable.set_snapshot_id(SNAPSHOT_ID_MAX);
410
411 if !Rc::ptr_eq(&head, &reusable) {
412 let mut cursor = Some(Rc::clone(&head));
413 let mut unlinked = false;
414
415 while let Some(node) = cursor {
416 let next = node.next();
417 if let Some(next_record) = next {
418 if Rc::ptr_eq(&next_record, &reusable) {
419 node.set_next(reusable.next());
420 unlinked = true;
421 break;
422 }
423 cursor = Some(next_record);
424 } else {
425 break;
426 }
427 }
428
429 if !unlinked {
430 debug_assert!(
431 false,
432 "new_overwritable_record_as_head_locked: reusable record not found in chain"
433 );
434 let new_record = StateRecord::new(SNAPSHOT_ID_MAX, (), None);
435 state.prepend_state_record(Rc::clone(&new_record));
436 return new_record;
437 }
438
439 state.prepend_state_record(Rc::clone(&reusable));
440 }
441
442 return reusable;
443 }
444
445 let new_record = StateRecord::new(SNAPSHOT_ID_MAX, (), None);
446 state.prepend_state_record(Rc::clone(&new_record));
447 new_record
448}
449
450pub(crate) fn overwrite_unused_records_locked<T: Any + Clone>(state: &dyn StateObject) -> bool {
464 let head = state.first_record();
465 let mut current = Some(Rc::clone(&head));
466 let mut overwrite_record: Option<Rc<StateRecord>> = None;
467 let mut valid_record: Option<Rc<StateRecord>> = None;
468
469 let reuse_limit =
472 lowest_pinned_snapshot().unwrap_or_else(crate::snapshot_v2::peek_next_snapshot_id);
473
474 let mut retained_records = 0;
475
476 while let Some(record) = current {
477 let current_id = record.snapshot_id();
478
479 if current_id == INVALID_SNAPSHOT_ID {
480 } else if current_id < reuse_limit {
482 if valid_record.is_none() {
483 valid_record = Some(Rc::clone(&record));
486 retained_records += 1;
487 } else {
488 let Some(valid) = valid_record.as_ref() else {
491 valid_record = Some(Rc::clone(&record));
492 retained_records += 1;
493 current = record.next();
494 continue;
495 };
496 let record_to_overwrite = if current_id < valid.snapshot_id() {
497 Rc::clone(&record)
498 } else {
499 let to_overwrite = Rc::clone(valid);
501 valid_record = Some(Rc::clone(&record));
502 to_overwrite
503 };
504
505 let source_record = overwrite_record.get_or_insert_with(|| {
507 find_youngest_or(&head, |r| r.snapshot_id() >= reuse_limit)
508 });
509
510 record_to_overwrite.set_snapshot_id(INVALID_SNAPSHOT_ID);
512 if let Err(error) = record_to_overwrite.assign_value::<T>(source_record) {
513 log::error!(
514 "snapshot cleanup could not copy retained state record value for state {:?}: {:?}",
515 state.object_id(),
516 error
517 );
518 }
519 }
520 } else {
521 retained_records += 1;
523 }
524
525 current = record.next();
526 }
527
528 retained_records > 1
531}
532
533fn active_snapshot() -> AnySnapshot {
534 current_snapshot().unwrap_or_else(|| AnySnapshot::Global(GlobalSnapshot::get_or_create()))
535}
536
537pub(crate) trait MutationPolicy<T>: Send + Sync {
538 fn equivalent(&self, a: &T, b: &T) -> bool;
539 fn merge(&self, _previous: &T, _current: &T, _applied: &T) -> Option<T> {
540 None
541 }
542}
543
544pub(crate) struct NeverEqual;
545
546impl<T> MutationPolicy<T> for NeverEqual {
547 fn equivalent(&self, _a: &T, _b: &T) -> bool {
548 false
549 }
550}
551
552pub trait StateObject: Any {
553 fn object_id(&self) -> ObjectId;
554 fn first_record(&self) -> Rc<StateRecord>;
555 fn try_readable_record(
556 &self,
557 snapshot_id: SnapshotId,
558 invalid: &SnapshotIdSet,
559 ) -> Option<Rc<StateRecord>>;
560 fn readable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord>;
561
562 fn prepend_state_record(&self, record: Rc<StateRecord>);
566
567 fn merge_records(
568 &self,
569 _previous: Rc<StateRecord>,
570 _current: Rc<StateRecord>,
571 _applied: Rc<StateRecord>,
572 ) -> Option<Rc<StateRecord>> {
573 None
574 }
575
576 fn commit_merged_record(&self, _merged: Rc<StateRecord>) -> Result<SnapshotId, &'static str> {
577 Err("StateObject does not support merged record commits")
578 }
579 fn promote_record(&self, child_id: SnapshotId) -> Result<(), &'static str>;
580
581 fn overwrite_unused_records(&self) -> bool {
586 false }
588
589 fn as_any(&self) -> &dyn Any;
591}
592
593pub(crate) struct SnapshotMutableState<T> {
594 head: CurrentRecord,
595 policy: Arc<dyn MutationPolicy<T>>,
596 id: ObjectId,
597 weak_self: Mutex<Option<Weak<Self>>>,
598 apply_observers: Mutex<Vec<Box<dyn Fn() + 'static>>>,
599}
600
601impl<T> SnapshotMutableState<T> {
602 fn assert_chain_integrity(&self, caller: &str, snapshot_context: Option<SnapshotId>) {
603 if !should_check_chain_integrity() {
604 return;
605 }
606 let head = self.head.clone_head();
607 let mut cursor = Some(head);
608 let mut seen: HashSet<usize> = HashSet::default();
609 let mut ids = Vec::new();
610
611 while let Some(record) = cursor {
612 let addr = Rc::as_ptr(&record) as usize;
613 assert!(
614 seen.insert(addr),
615 "SnapshotMutableState::{} detected duplicate/cycle at record {:p} for state {:?} (snapshot_context={:?}, chain_ids={:?})",
616 caller,
617 Rc::as_ptr(&record),
618 self.id,
619 snapshot_context,
620 ids
621 );
622 ids.push(record.snapshot_id());
623 cursor = record.next();
624 }
625
626 assert!(
627 !ids.is_empty(),
628 "SnapshotMutableState::{} finished integrity scan with empty id list for state {:?} (snapshot_context={:?})",
629 caller,
630 self.id,
631 snapshot_context
632 );
633 }
634}
635
636fn should_check_chain_integrity() -> bool {
637 #[cfg(debug_assertions)]
638 {
639 true
640 }
641
642 #[cfg(not(debug_assertions))]
643 {
644 std::env::var_os("CRANPOSE_ASSERT_STATE_CHAIN").is_some()
645 }
646}
647
648impl<T: Clone + 'static> SnapshotMutableState<T> {
649 fn record_chain_debug(&self) -> Vec<(SnapshotId, bool)> {
650 let mut chain_ids = Vec::new();
651 let mut cursor = Some(self.first_record());
652 while let Some(record) = cursor {
653 chain_ids.push((record.snapshot_id(), record.is_tombstone()));
654 cursor = record.next();
655 }
656 chain_ids
657 }
658
659 fn readable_record_for_active_snapshot(&self) -> Result<Rc<StateRecord>, StateReadFailure> {
660 let snapshot = active_snapshot();
661 if let Some(state) = self.upgrade_self() {
662 snapshot.record_read(&*state);
663 }
664
665 let snapshot_id = snapshot.snapshot_id();
666 let invalid = snapshot.invalid();
667
668 if let Some(record) = self.readable_for(snapshot_id, &invalid) {
669 return Ok(record);
670 }
671
672 let fresh_snapshot = active_snapshot();
673 let fresh_id = fresh_snapshot.snapshot_id();
674 let fresh_invalid = fresh_snapshot.invalid();
675
676 if let Some(record) = self.readable_for(fresh_id, &fresh_invalid) {
677 return Ok(record);
678 }
679
680 let global = GlobalSnapshot::get_or_create();
681 let global_id = global.snapshot_id();
682 let global_invalid = global.invalid();
683
684 if let Some(record) = self.readable_for(global_id, &global_invalid) {
685 return Ok(record);
686 }
687
688 Err(StateReadFailure {
689 state_id: self.id,
690 snapshot_id,
691 fresh_snapshot_id: fresh_id,
692 fresh_invalid,
693 record_chain: self.record_chain_debug(),
694 })
695 }
696
697 fn readable_for(
698 &self,
699 snapshot_id: SnapshotId,
700 invalid: &SnapshotIdSet,
701 ) -> Option<Rc<StateRecord>> {
702 let head = self.first_record();
703 readable_record_for(&head, snapshot_id, invalid)
704 }
705
706 fn writable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord> {
707 let readable = match self.readable_for(snapshot_id, invalid) {
708 Some(record) => record,
709 None => {
710 let current_head = self.head.clone_head();
711 let refreshed = readable_record_for(¤t_head, snapshot_id, invalid);
712 let source = refreshed.unwrap_or_else(|| current_head.clone());
713
714 let cloned_value = source.with_value(|value: &T| value.clone());
718 let new_head = StateRecord::new(snapshot_id, cloned_value, Some(current_head));
719 self.head.replace(new_head.clone());
720 self.assert_chain_integrity("writable_record(recover)", Some(snapshot_id));
721 return new_head;
722 }
723 };
724
725 if readable.snapshot_id() == snapshot_id {
726 return readable;
727 }
728
729 let refreshed = {
730 let current_head = self.head.clone_head();
731 let refreshed = readable_record_for(¤t_head, snapshot_id, invalid).unwrap_or_else(
732 || {
733 panic!(
734 "SnapshotMutableState::writable_record failed to locate refreshed readable record (state {:?}, snapshot_id={}, invalid={:?})",
735 self.id, snapshot_id, invalid
736 )
737 },
738 );
739
740 if refreshed.snapshot_id() == snapshot_id {
741 return refreshed;
742 }
743
744 Rc::clone(&refreshed)
745 };
746
747 let overwritable = new_overwritable_record_locked(self);
748 if let Err(error) = overwritable.assign_value::<T>(&refreshed) {
749 log::error!(
750 "snapshot writable record could not copy refreshed value for state {:?}: {:?}",
751 self.id,
752 error
753 );
754 }
755 overwritable.set_snapshot_id(snapshot_id);
756 overwritable.set_tombstone(false);
757
758 self.assert_chain_integrity("writable_record(reuse)", Some(snapshot_id));
759
760 overwritable
761 }
762
763 pub(crate) fn new_in_arc(initial: T, policy: Arc<dyn MutationPolicy<T>>) -> Arc<Self> {
764 let snapshot = active_snapshot();
765 let snapshot_id = snapshot.snapshot_id();
766
767 let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, initial.clone(), None);
768 let head = StateRecord::new(snapshot_id, initial, Some(tail));
769
770 let mut state = Arc::new(Self {
771 head: CurrentRecord::new(head),
772 policy,
773 id: ObjectId::default(),
774 weak_self: Mutex::new(None),
775 apply_observers: Mutex::new(Vec::new()),
776 });
777
778 let id = ObjectId::new(&state);
779 if let Some(state_inner) = Arc::get_mut(&mut state) {
780 state_inner.id = id;
781 }
782
783 *state.lock_weak_self() = Some(Arc::downgrade(&state));
784
785 state
788 }
789
790 pub(crate) fn add_apply_observer(&self, observer: Box<dyn Fn() + 'static>) {
791 self.lock_apply_observers().push(observer);
792 }
793
794 fn notify_applied(&self) {
795 let observers = self.lock_apply_observers();
796 for observer in observers.iter() {
797 observer();
798 }
799 }
800
801 fn lock_weak_self(&self) -> MutexGuard<'_, Option<Weak<Self>>> {
802 self.weak_self
803 .lock()
804 .unwrap_or_else(|poisoned| poisoned.into_inner())
805 }
806
807 fn lock_apply_observers(&self) -> MutexGuard<'_, Vec<Box<dyn Fn() + 'static>>> {
808 self.apply_observers
809 .lock()
810 .unwrap_or_else(|poisoned| poisoned.into_inner())
811 }
812
813 fn upgrade_self(&self) -> Option<Arc<Self>> {
814 self.lock_weak_self()
815 .as_ref()
816 .and_then(|weak| weak.upgrade())
817 }
818
819 #[inline]
820 pub(crate) fn id(&self) -> ObjectId {
821 self.id
822 }
823
824 pub(crate) fn try_with_value<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
825 let record = self.readable_record_for_active_snapshot().ok()?;
826 record.try_with_value(f)
827 }
828
829 pub(crate) fn try_get(&self) -> Option<T> {
830 self.try_with_value(Clone::clone)
831 }
832
833 pub(crate) fn get(&self) -> T {
834 let record = self
835 .readable_record_for_active_snapshot()
836 .unwrap_or_else(|failure| panic!("{failure}"));
837 record.with_value(|value: &T| value.clone())
838 }
839
840 pub(crate) fn set(&self, new_value: T) -> bool {
841 #[cfg(debug_assertions)]
843 {
844 let in_handler = crate::in_event_handler();
845 let in_snapshot = crate::in_applied_snapshot();
846 if in_handler && !in_snapshot {
847 log::warn!(
848 target: "cranpose::state",
849 "State modified in event handler without run_in_mutable_snapshot; \
850 this can make updates invisible to other contexts. Wrap the handler \
851 in run_in_mutable_snapshot() or dispatch_ui_event(). State: {:?}",
852 self.id
853 );
854 }
855 }
856
857 let snapshot = active_snapshot();
858 let snapshot_id = snapshot.snapshot_id();
859
860 match &snapshot {
861 AnySnapshot::Global(global) => {
862 let invalid = snapshot.invalid();
863 let equivalent = self
864 .readable_for(snapshot_id, &invalid)
865 .map(|record| {
866 record.with_value(|current: &T| self.policy.equivalent(current, &new_value))
867 })
868 .unwrap_or(false);
869 if equivalent {
870 return false;
871 }
872
873 if global.has_pending_children() {
874 panic!(
875 "SnapshotMutableState::set attempted global write while pending children {:?} exist (state {:?}, snapshot_id={})",
876 global.pending_children(),
877 self.id,
878 snapshot_id
879 );
880 }
881
882 let mut written_state: Option<Arc<dyn StateObject>> = None;
883 if let Some(state) = self.upgrade_self() {
884 let trait_object: Arc<dyn StateObject> = state.clone();
885 snapshot.record_write(trait_object.clone());
886 written_state = Some(trait_object);
887 }
888 mark_update_write(self.id);
889
890 let new_id = allocate_record_id();
891 let record = new_overwritable_record_as_head_locked(self);
892 record.replace_value(new_value);
893 record.set_snapshot_id(new_id);
894 record.set_tombstone(false);
895 advance_global_snapshot(new_id);
896 self.assert_chain_integrity("set(global-push)", Some(snapshot_id));
897
898 if !global.has_pending_children() {
899 let mut cursor = record.next();
900 while let Some(node) = cursor {
901 if !node.is_tombstone() && node.snapshot_id() != PREEXISTING_SNAPSHOT_ID {
902 node.clear_value();
903 node.set_tombstone(true);
904 }
905 cursor = node.next();
906 }
907 self.assert_chain_integrity("set(global-tombstone)", Some(snapshot_id));
908 }
909
910 if let Some(modified) = written_state.as_ref() {
911 crate::snapshot_v2::notify_apply_observers(
912 std::slice::from_ref(modified),
913 new_id,
914 );
915 }
916 }
917 AnySnapshot::Mutable(_)
918 | AnySnapshot::NestedMutable(_)
919 | AnySnapshot::TransparentMutable(_) => {
920 let invalid = snapshot.invalid();
921 let equivalent = self
922 .readable_for(snapshot_id, &invalid)
923 .map(|record| {
924 record.with_value(|current: &T| self.policy.equivalent(current, &new_value))
925 })
926 .unwrap_or(false);
927 if equivalent {
928 return false;
929 }
930
931 if let Some(state) = self.upgrade_self() {
932 let trait_object: Arc<dyn StateObject> = state.clone();
933 snapshot.record_write(trait_object);
934 }
935 mark_update_write(self.id);
936
937 let record = self.writable_record(snapshot_id, &invalid);
938 record.replace_value(new_value);
939 self.assert_chain_integrity("set(child-writable)", Some(snapshot_id));
940 }
941 AnySnapshot::Readonly(_)
942 | AnySnapshot::NestedReadonly(_)
943 | AnySnapshot::TransparentReadonly(_) => {
944 panic!("Cannot write to a read-only snapshot");
945 }
946 }
947
948 true
953 }
954}
955
956thread_local! {
957 static ACTIVE_UPDATES: RefCell<HashSet<ObjectId>> = RefCell::new(HashSet::default());
958 static PENDING_WRITES: RefCell<HashSet<ObjectId>> = RefCell::new(HashSet::default());
959}
960
961pub(crate) struct UpdateScope {
962 id: ObjectId,
963 finished: bool,
964}
965
966impl UpdateScope {
967 pub(crate) fn new(id: ObjectId) -> Self {
968 ACTIVE_UPDATES.with(|active| {
969 active.borrow_mut().insert(id);
970 });
971 PENDING_WRITES.with(|pending| {
972 pending.borrow_mut().remove(&id);
973 });
974 Self {
975 id,
976 finished: false,
977 }
978 }
979
980 pub(crate) fn finish(mut self) -> bool {
981 self.finished = true;
982 ACTIVE_UPDATES.with(|active| {
983 active.borrow_mut().remove(&self.id);
984 });
985 PENDING_WRITES.with(|pending| pending.borrow_mut().remove(&self.id))
986 }
987}
988
989impl Drop for UpdateScope {
990 fn drop(&mut self) {
991 if self.finished {
992 return;
993 }
994 ACTIVE_UPDATES.with(|active| {
995 active.borrow_mut().remove(&self.id);
996 });
997 PENDING_WRITES.with(|pending| {
998 pending.borrow_mut().remove(&self.id);
999 });
1000 }
1001}
1002
1003fn mark_update_write(id: ObjectId) {
1004 ACTIVE_UPDATES.with(|active| {
1005 if active.borrow().contains(&id) {
1006 PENDING_WRITES.with(|pending| {
1007 pending.borrow_mut().insert(id);
1008 });
1009 }
1010 });
1011}
1012
1013impl<T: Clone + 'static> SnapshotMutableState<T> {
1014 fn try_readable_record(
1016 &self,
1017 snapshot_id: SnapshotId,
1018 invalid: &SnapshotIdSet,
1019 ) -> Option<Rc<StateRecord>> {
1020 self.readable_for(snapshot_id, invalid)
1021 }
1022}
1023
1024impl<T: Clone + 'static> StateObject for SnapshotMutableState<T> {
1025 fn object_id(&self) -> ObjectId {
1026 self.id
1027 }
1028
1029 fn first_record(&self) -> Rc<StateRecord> {
1030 self.head.clone_head()
1031 }
1032
1033 fn try_readable_record(
1034 &self,
1035 snapshot_id: SnapshotId,
1036 invalid: &SnapshotIdSet,
1037 ) -> Option<Rc<StateRecord>> {
1038 self.try_readable_record(snapshot_id, invalid)
1039 }
1040
1041 fn readable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord> {
1042 self.try_readable_record(snapshot_id, invalid)
1043 .unwrap_or_else(|| {
1044 panic!(
1045 "SnapshotMutableState::readable_record returned null (state={:?}, snapshot_id={})",
1046 self.id, snapshot_id
1047 )
1048 })
1049 }
1050
1051 fn prepend_state_record(&self, record: Rc<StateRecord>) {
1052 self.head.prepend(record);
1053 }
1054
1055 fn merge_records(
1056 &self,
1057 previous: Rc<StateRecord>,
1058 current: Rc<StateRecord>,
1059 applied: Rc<StateRecord>,
1060 ) -> Option<Rc<StateRecord>> {
1061 let Some(current_value) = current.try_with_value(|value: &T| value.clone()) else {
1062 log::error!(
1063 "SnapshotMutableState::merge_records current record value missing or wrong type (state {:?}, current_id={})",
1064 self.id,
1065 current.snapshot_id()
1066 );
1067 return None;
1068 };
1069 let Some(applied_value) = applied.try_with_value(|value: &T| value.clone()) else {
1070 log::error!(
1071 "SnapshotMutableState::merge_records applied record value missing or wrong type (state {:?}, applied_id={})",
1072 self.id,
1073 applied.snapshot_id()
1074 );
1075 return None;
1076 };
1077 if self.policy.equivalent(¤t_value, &applied_value) {
1078 return Some(current);
1079 }
1080
1081 let Some(previous_value) = previous.try_with_value(|value: &T| value.clone()) else {
1082 log::error!(
1083 "SnapshotMutableState::merge_records previous record value missing or wrong type (state {:?}, previous_id={})",
1084 self.id,
1085 previous.snapshot_id()
1086 );
1087 return None;
1088 };
1089 let merged = self
1090 .policy
1091 .merge(&previous_value, ¤t_value, &applied_value)?;
1092
1093 Some(StateRecord::new(applied.snapshot_id(), merged, None))
1094 }
1095
1096 fn promote_record(&self, child_id: SnapshotId) -> Result<(), &'static str> {
1097 let head = self.first_record();
1098 let mut cursor = Some(head);
1099 while let Some(record) = cursor {
1100 if record.snapshot_id() == child_id {
1101 let Some(cloned) = record.try_with_value(|value: &T| value.clone()) else {
1102 log::error!(
1103 "SnapshotMutableState::promote_record child record value missing or wrong type (state {:?}, child_id={})",
1104 self.id,
1105 child_id
1106 );
1107 return Err("child record value missing or wrong type");
1108 };
1109 let new_id = allocate_record_id();
1110 let current_head = self.head.clone_head();
1111 let new_head = StateRecord::new(new_id, cloned, Some(current_head));
1112 self.head.replace(new_head);
1113 advance_global_snapshot(new_id);
1114 self.notify_applied();
1115 self.assert_chain_integrity("promote_record", Some(child_id));
1116 return Ok(());
1117 }
1118 cursor = record.next();
1119 }
1120 log::error!(
1121 "SnapshotMutableState::promote_record missing child record (state {:?}, child_id={})",
1122 self.id,
1123 child_id
1124 );
1125 Err("missing child record")
1126 }
1127
1128 fn commit_merged_record(&self, merged: Rc<StateRecord>) -> Result<SnapshotId, &'static str> {
1129 let Some(value) = merged.try_with_value(|value: &T| value.clone()) else {
1130 log::error!(
1131 "SnapshotMutableState::commit_merged_record merged record value missing or wrong type (state {:?}, merged_id={})",
1132 self.id,
1133 merged.snapshot_id()
1134 );
1135 return Err("merged record value missing or wrong type");
1136 };
1137 let new_id = allocate_record_id();
1138 let current_head = self.head.clone_head();
1139 let new_head = StateRecord::new(new_id, value, Some(current_head));
1140 self.head.replace(new_head);
1141 advance_global_snapshot(new_id);
1142 self.notify_applied();
1143 self.assert_chain_integrity("commit_merged_record", Some(new_id));
1144 Ok(new_id)
1145 }
1146
1147 fn overwrite_unused_records(&self) -> bool {
1148 overwrite_unused_records_locked::<T>(self)
1149 }
1150
1151 fn as_any(&self) -> &dyn Any {
1152 self
1153 }
1154}
1155
1156pub(crate) struct MutableStateInner<T: Clone + 'static> {
1157 pub(crate) state: Arc<SnapshotMutableState<T>>,
1158 pub(crate) watchers: RefCell<HashMap<ScopeId, RcWeak<crate::RecomposeScopeInner>>>,
1159 runtime: RuntimeHandle,
1160 state_id: Cell<Option<StateId>>,
1161}
1162
1163fn shrink_watchers_if_sparse(watchers: &mut HashMap<ScopeId, RcWeak<crate::RecomposeScopeInner>>) {
1164 let len = watchers.len();
1165 let capacity = watchers.capacity();
1166 if capacity > len.saturating_mul(4).max(32) {
1167 watchers.shrink_to_fit();
1168 }
1169}
1170
1171impl<T: Clone + 'static> MutableStateInner<T> {
1172 pub(crate) fn new_with_policy(
1173 value: T,
1174 runtime: RuntimeHandle,
1175 policy: Arc<dyn MutationPolicy<T>>,
1176 ) -> Self {
1177 Self {
1178 state: SnapshotMutableState::new_in_arc(value, policy),
1179 watchers: RefCell::new(HashMap::default()),
1180 runtime,
1181 state_id: Cell::new(None),
1182 }
1183 }
1184
1185 pub(crate) fn install_snapshot_observer(&self, state_id: StateId) {
1186 self.state_id.set(Some(state_id));
1187 let runtime_handle = self.runtime.clone();
1188 self.state.add_apply_observer(Box::new(move || {
1189 let runtime = runtime_handle.clone();
1190 runtime_handle.enqueue_ui_task(Box::new(move || {
1191 runtime.with_state_arena(|arena| {
1192 let _ = arena.with_typed_opt::<T, _>(state_id, |inner| {
1193 inner.invalidate_watchers();
1194 });
1195 });
1196 }));
1197 }));
1198 }
1199
1200 fn with_value<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1201 let value = self.state.get();
1202 f(&value)
1203 }
1204
1205 fn register_scope(&self, scope: &RecomposeScope) -> bool {
1206 let mut watchers = self.watchers.borrow_mut();
1207 match watchers.get(&scope.id()) {
1208 Some(existing) if existing.upgrade().is_some() => false,
1209 _ => {
1210 watchers.insert(scope.id(), scope.downgrade());
1211 true
1212 }
1213 }
1214 }
1215
1216 pub(crate) fn unregister_scope(&self, scope_id: ScopeId) {
1217 let mut watchers = self.watchers.borrow_mut();
1218 watchers.remove(&scope_id);
1219 shrink_watchers_if_sparse(&mut watchers);
1220 }
1221
1222 fn state_id(&self) -> Option<StateId> {
1223 self.state_id.get()
1224 }
1225
1226 fn invalidate_watchers(&self) {
1227 let watchers: Vec<RecomposeScope> = {
1228 let mut watchers = self.watchers.borrow_mut();
1229 let mut live = Vec::with_capacity(watchers.len());
1230 watchers.retain(|_, weak| {
1231 if let Some(inner) = weak.upgrade() {
1232 live.push(RecomposeScope { inner });
1233 true
1234 } else {
1235 false
1236 }
1237 });
1238 shrink_watchers_if_sparse(&mut watchers);
1239 live
1240 };
1241
1242 for watcher in watchers {
1243 debug_record_scope_invalidation::<T>(watcher.id(), self.state_id.get());
1244 watcher.invalidate();
1245 }
1246 }
1247}
1248
1249fn register_current_state_scope<T: Clone + 'static>(inner: &MutableStateInner<T>) {
1250 let Some(Some(scope)) =
1251 with_current_composer_opt(|composer| composer.current_recranpose_scope())
1252 else {
1253 return;
1254 };
1255 if inner.register_scope(&scope) {
1256 if let Some(state_id) = inner.state_id() {
1257 scope.record_state_subscription(state_id);
1258 }
1259 }
1260}
1261
1262pub struct State<T: Clone + 'static> {
1264 id: StateId,
1265 runtime_id: runtime::RuntimeId,
1266 _marker: PhantomData<fn() -> T>,
1267}
1268
1269pub struct MutableState<T: Clone + 'static> {
1275 id: StateId,
1276 runtime_id: runtime::RuntimeId,
1277 _marker: PhantomData<fn() -> T>,
1278}
1279
1280#[derive(Clone)]
1282pub struct OwnedMutableState<T: Clone + 'static> {
1283 state: MutableState<T>,
1284 _lease: Rc<runtime::StateHandleLease>,
1285 _marker: PhantomData<fn() -> T>,
1286}
1287
1288impl<T: Clone + 'static> PartialEq for State<T> {
1289 fn eq(&self, other: &Self) -> bool {
1290 self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
1291 }
1292}
1293
1294impl<T: Clone + 'static> Eq for State<T> {}
1295
1296impl<T: Clone + 'static> PartialEq for MutableState<T> {
1297 fn eq(&self, other: &Self) -> bool {
1298 self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
1299 }
1300}
1301
1302impl<T: Clone + 'static> Eq for MutableState<T> {}
1303
1304impl<T: Clone + 'static> Copy for State<T> {}
1305
1306impl<T: Clone + 'static> Clone for State<T> {
1307 fn clone(&self) -> Self {
1308 *self
1309 }
1310}
1311
1312impl<T: Clone + 'static> Copy for MutableState<T> {}
1313
1314impl<T: Clone + 'static> Clone for MutableState<T> {
1315 fn clone(&self) -> Self {
1316 *self
1317 }
1318}
1319
1320impl<T: Clone + 'static> State<T> {
1321 fn state_id(&self) -> StateId {
1322 self.id
1323 }
1324
1325 fn runtime_id(&self) -> runtime::RuntimeId {
1326 self.runtime_id
1327 }
1328
1329 fn runtime_handle(&self) -> RuntimeHandle {
1330 runtime::runtime_handle_by_id(self.runtime_id())
1331 .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
1332 }
1333
1334 fn runtime_handle_opt(&self) -> Option<RuntimeHandle> {
1335 runtime::runtime_handle_by_id(self.runtime_id())
1336 }
1337
1338 fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1339 self.runtime_handle()
1340 .with_state_arena(|arena| arena.with_typed::<T, R>(self.state_id(), f))
1341 }
1342
1343 fn try_with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
1344 self.runtime_handle_opt()?
1345 .try_with_state_arena(|arena| arena.with_typed_opt::<T, R>(self.state_id(), f))?
1346 }
1347
1348 fn subscribe_current_scope(&self) {
1349 self.with_inner(register_current_state_scope::<T>);
1350 }
1351
1352 pub fn is_alive(&self) -> bool {
1353 self.try_with_inner(|_| ()).is_some()
1354 }
1355
1356 pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1357 self.try_with_inner(|inner| inner.state.try_with_value(f))?
1358 }
1359
1360 pub fn try_value(&self) -> Option<T> {
1361 self.try_with_inner(|inner| inner.state.try_get())?
1362 }
1363
1364 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1365 self.subscribe_current_scope();
1366 self.with_inner(|inner| inner.with_value(f))
1367 }
1368
1369 pub fn value(&self) -> T {
1370 self.subscribe_current_scope();
1371 self.with_inner(|inner| inner.state.get())
1372 }
1373
1374 pub fn get(&self) -> T {
1375 self.value()
1376 }
1377}
1378
1379impl<T: Clone + 'static> MutableState<T> {
1380 pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
1381 runtime.alloc_persistent_state(value)
1382 }
1383
1384 fn from_parts(id: StateId, runtime_id: runtime::RuntimeId) -> Self {
1385 Self {
1386 id,
1387 runtime_id,
1388 _marker: PhantomData,
1389 }
1390 }
1391
1392 pub(crate) fn from_lease(lease: &Rc<runtime::StateHandleLease>) -> Self {
1393 Self::from_parts(lease.id(), lease.runtime().id())
1394 }
1395
1396 fn state_id(&self) -> StateId {
1397 self.id
1398 }
1399
1400 fn runtime_id(&self) -> runtime::RuntimeId {
1401 self.runtime_id
1402 }
1403
1404 fn runtime_handle(&self) -> RuntimeHandle {
1405 runtime::runtime_handle_by_id(self.runtime_id())
1406 .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
1407 }
1408
1409 fn runtime_handle_opt(&self) -> Option<RuntimeHandle> {
1410 runtime::runtime_handle_by_id(self.runtime_id())
1411 }
1412
1413 fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1414 self.runtime_handle()
1415 .with_state_arena(|arena| arena.with_typed::<T, R>(self.state_id(), f))
1416 }
1417
1418 fn try_with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
1419 self.runtime_handle_opt()?
1420 .try_with_state_arena(|arena| arena.with_typed_opt::<T, R>(self.state_id(), f))?
1421 }
1422
1423 pub fn is_alive(&self) -> bool {
1424 self.try_with_inner(|_| ()).is_some()
1425 }
1426
1427 pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1428 self.try_with_inner(|inner| inner.state.try_with_value(f))?
1429 }
1430
1431 pub fn try_value(&self) -> Option<T> {
1432 self.try_with_inner(|inner| inner.state.try_get())?
1433 }
1434
1435 pub fn as_state(&self) -> State<T> {
1436 State {
1437 id: self.id,
1438 runtime_id: self.runtime_id,
1439 _marker: PhantomData,
1440 }
1441 }
1442
1443 pub fn try_retain(&self) -> Option<OwnedMutableState<T>> {
1444 let lease = self
1445 .runtime_handle_opt()?
1446 .retain_state_lease(self.state_id())?;
1447 Some(OwnedMutableState {
1448 state: *self,
1449 _lease: lease,
1450 _marker: PhantomData,
1451 })
1452 }
1453
1454 pub fn retain(&self) -> OwnedMutableState<T> {
1455 self.try_retain()
1456 .unwrap_or_else(|| panic!("state {:?} is no longer alive", self.state_id()))
1457 }
1458
1459 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1460 self.subscribe_current_scope();
1461 self.with_inner(|inner| inner.with_value(f))
1462 }
1463
1464 pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
1465 let runtime = self.runtime_handle();
1466 runtime.assert_ui_thread();
1467 runtime.with_state_arena(|arena| {
1468 arena.with_typed::<T, R>(self.state_id(), |inner| {
1469 let mut value = inner.state.get();
1470 let tracker = UpdateScope::new(inner.state.id());
1471 let result = f(&mut value);
1472 let wrote_elsewhere = tracker.finish();
1473 if !wrote_elsewhere && inner.state.set(value) {
1474 inner.invalidate_watchers();
1475 }
1476 result
1477 })
1478 })
1479 }
1480
1481 pub fn replace(&self, value: T) {
1482 let Some(runtime) = self.runtime_handle_opt() else {
1483 log::debug!(
1484 "MutableState::replace skipped: runtime {:?} dropped",
1485 self.runtime_id()
1486 );
1487 return;
1488 };
1489 runtime.assert_ui_thread();
1490 let replaced = runtime
1491 .try_with_state_arena(|arena| {
1492 arena.with_typed_opt::<T, ()>(self.state_id(), |inner| {
1493 if inner.state.set(value) {
1494 inner.invalidate_watchers();
1495 }
1496 })
1497 })
1498 .flatten();
1499 if replaced.is_none() {
1500 log::debug!(
1501 "MutableState::replace skipped: state cell released (slot={}, gen={})",
1502 self.state_id().slot(),
1503 self.state_id().generation(),
1504 );
1505 }
1506 }
1507
1508 pub fn set_value(&self, value: T) {
1509 self.replace(value);
1510 }
1511
1512 pub fn set(&self, value: T) {
1513 self.replace(value);
1514 }
1515
1516 pub fn value(&self) -> T {
1517 self.subscribe_current_scope();
1518 self.with_inner(|inner| inner.state.get())
1519 }
1520
1521 pub fn get(&self) -> T {
1522 self.value()
1523 }
1524
1525 pub fn get_non_reactive(&self) -> T {
1526 self.with_inner(|inner| inner.state.get())
1527 }
1528
1529 fn subscribe_current_scope(&self) {
1530 self.with_inner(register_current_state_scope::<T>);
1531 }
1532
1533 #[cfg(test)]
1534 pub(crate) fn watcher_count(&self) -> usize {
1535 self.with_inner(|inner| inner.watchers.borrow().len())
1536 }
1537
1538 #[cfg(test)]
1539 pub(crate) fn watcher_capacity(&self) -> usize {
1540 self.with_inner(|inner| inner.watchers.borrow().capacity())
1541 }
1542
1543 #[cfg(test)]
1544 pub(crate) fn state_id_for_test(&self) -> StateId {
1545 self.state_id()
1546 }
1547
1548 #[cfg(test)]
1549 pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
1550 self.as_state().subscribe_scope_for_test(scope);
1551 }
1552}
1553
1554impl<T: Clone + 'static> OwnedMutableState<T> {
1555 pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
1556 let lease = runtime.alloc_state(value);
1557 Self {
1558 state: MutableState::from_lease(&lease),
1559 _lease: lease,
1560 _marker: PhantomData,
1561 }
1562 }
1563
1564 pub(crate) fn with_runtime_and_policy(
1565 value: T,
1566 runtime: RuntimeHandle,
1567 policy: Arc<dyn MutationPolicy<T>>,
1568 ) -> Self {
1569 let lease = runtime.alloc_state_with_policy(value, policy);
1570 Self {
1571 state: MutableState::from_lease(&lease),
1572 _lease: lease,
1573 _marker: PhantomData,
1574 }
1575 }
1576
1577 pub fn handle(&self) -> MutableState<T> {
1578 self.state
1579 }
1580
1581 pub fn as_state(&self) -> State<T> {
1582 self.state.as_state()
1583 }
1584}
1585
1586impl<T: Clone + 'static> Deref for OwnedMutableState<T> {
1587 type Target = MutableState<T>;
1588
1589 fn deref(&self) -> &Self::Target {
1590 &self.state
1591 }
1592}
1593
1594#[cfg(test)]
1595impl<T: Clone + 'static> State<T> {
1596 pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
1597 self.with_inner(|inner| {
1598 if inner.register_scope(scope) {
1599 if let Some(state_id) = inner.state_id() {
1600 scope.record_state_subscription(state_id);
1601 }
1602 }
1603 });
1604 }
1605}
1606
1607impl<T: fmt::Debug + Clone + 'static> fmt::Debug for MutableState<T> {
1608 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1609 if let Some(value) = self.try_value() {
1610 f.debug_struct("MutableState")
1611 .field("value", &value)
1612 .finish()
1613 } else {
1614 f.write_str("MutableState { value: <unavailable> }")
1615 }
1616 }
1617}
1618
1619#[derive(Clone)]
1620pub struct SnapshotStateList<T: Clone + 'static> {
1621 state: OwnedMutableState<Vec<T>>,
1622}
1623
1624impl<T: Clone + 'static> SnapshotStateList<T> {
1625 pub fn with_runtime<I>(values: I, runtime: RuntimeHandle) -> Self
1626 where
1627 I: IntoIterator<Item = T>,
1628 {
1629 let initial: Vec<T> = values.into_iter().collect();
1630 Self {
1631 state: OwnedMutableState::with_runtime(initial, runtime),
1632 }
1633 }
1634
1635 pub fn as_state(&self) -> State<Vec<T>> {
1636 self.state.as_state()
1637 }
1638
1639 pub fn as_mutable_state(&self) -> MutableState<Vec<T>> {
1640 self.state.handle()
1641 }
1642
1643 pub fn len(&self) -> usize {
1644 self.state.with(|values| values.len())
1645 }
1646
1647 pub fn is_empty(&self) -> bool {
1648 self.len() == 0
1649 }
1650
1651 pub fn to_vec(&self) -> Vec<T> {
1652 self.state.with(|values| values.clone())
1653 }
1654
1655 pub fn iter(&self) -> Vec<T> {
1656 self.to_vec()
1657 }
1658
1659 pub fn get(&self, index: usize) -> T {
1660 self.state.with(|values| values[index].clone())
1661 }
1662
1663 pub fn get_opt(&self, index: usize) -> Option<T> {
1664 self.state.with(|values| values.get(index).cloned())
1665 }
1666
1667 pub fn first(&self) -> Option<T> {
1668 self.get_opt(0)
1669 }
1670
1671 pub fn last(&self) -> Option<T> {
1672 self.state.with(|values| values.last().cloned())
1673 }
1674
1675 pub fn push(&self, value: T) {
1676 self.state.update(|values| values.push(value));
1677 }
1678
1679 pub fn extend<I>(&self, iter: I)
1680 where
1681 I: IntoIterator<Item = T>,
1682 {
1683 self.state.update(|values| values.extend(iter));
1684 }
1685
1686 pub fn insert(&self, index: usize, value: T) {
1687 self.state.update(|values| values.insert(index, value));
1688 }
1689
1690 pub fn set(&self, index: usize, value: T) -> T {
1691 self.state
1692 .update(|values| std::mem::replace(&mut values[index], value))
1693 }
1694
1695 pub fn remove(&self, index: usize) -> T {
1696 self.state.update(|values| values.remove(index))
1697 }
1698
1699 pub fn pop(&self) -> Option<T> {
1700 self.state.update(|values| values.pop())
1701 }
1702
1703 pub fn clear(&self) {
1704 self.state.replace(Vec::new());
1705 }
1706
1707 pub fn retain<F>(&self, mut predicate: F)
1708 where
1709 F: FnMut(&T) -> bool,
1710 {
1711 self.state
1712 .update(|values| values.retain(|value| predicate(value)));
1713 }
1714
1715 pub fn replace_with<I>(&self, iter: I)
1716 where
1717 I: IntoIterator<Item = T>,
1718 {
1719 self.state.replace(iter.into_iter().collect());
1720 }
1721}
1722
1723impl<T: fmt::Debug + Clone + 'static> fmt::Debug for SnapshotStateList<T> {
1724 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1725 let contents = self.to_vec();
1726 f.debug_struct("SnapshotStateList")
1727 .field("values", &contents)
1728 .finish()
1729 }
1730}
1731
1732#[derive(Clone)]
1733pub struct SnapshotStateMap<K, V>
1734where
1735 K: Clone + Eq + Hash + 'static,
1736 V: Clone + 'static,
1737{
1738 state: OwnedMutableState<HashMap<K, V>>,
1739}
1740
1741impl<K, V> SnapshotStateMap<K, V>
1742where
1743 K: Clone + Eq + Hash + 'static,
1744 V: Clone + 'static,
1745{
1746 pub fn with_runtime<I>(pairs: I, runtime: RuntimeHandle) -> Self
1747 where
1748 I: IntoIterator<Item = (K, V)>,
1749 {
1750 let map: HashMap<K, V> = pairs.into_iter().collect();
1751 Self {
1752 state: OwnedMutableState::with_runtime(map, runtime),
1753 }
1754 }
1755
1756 pub fn as_state(&self) -> State<HashMap<K, V>> {
1757 self.state.as_state()
1758 }
1759
1760 pub fn as_mutable_state(&self) -> MutableState<HashMap<K, V>> {
1761 self.state.handle()
1762 }
1763
1764 pub fn len(&self) -> usize {
1765 self.state.with(|map| map.len())
1766 }
1767
1768 pub fn is_empty(&self) -> bool {
1769 self.state.with(|map| map.is_empty())
1770 }
1771
1772 pub fn contains_key(&self, key: &K) -> bool {
1773 self.state.with(|map| map.contains_key(key))
1774 }
1775
1776 pub fn get(&self, key: &K) -> Option<V> {
1777 self.state.with(|map| map.get(key).cloned())
1778 }
1779
1780 pub fn to_hash_map(&self) -> HashMap<K, V> {
1781 self.state.with(|map| map.clone())
1782 }
1783
1784 pub fn insert(&self, key: K, value: V) -> Option<V> {
1785 self.state.update(|map| map.insert(key, value))
1786 }
1787
1788 pub fn extend<I>(&self, iter: I)
1789 where
1790 I: IntoIterator<Item = (K, V)>,
1791 {
1792 self.state.update(|map| map.extend(iter));
1793 }
1794
1795 pub fn remove(&self, key: &K) -> Option<V> {
1796 self.state.update(|map| map.remove(key))
1797 }
1798
1799 pub fn clear(&self) {
1800 self.state.replace(HashMap::default());
1801 }
1802
1803 pub fn retain<F>(&self, mut predicate: F)
1804 where
1805 F: FnMut(&K, &mut V) -> bool,
1806 {
1807 self.state.update(|map| map.retain(|k, v| predicate(k, v)));
1808 }
1809}
1810
1811impl<K, V> fmt::Debug for SnapshotStateMap<K, V>
1812where
1813 K: Clone + Eq + Hash + fmt::Debug + 'static,
1814 V: Clone + fmt::Debug + 'static,
1815{
1816 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1817 let contents = self.to_hash_map();
1818 f.debug_struct("SnapshotStateMap")
1819 .field("entries", &contents)
1820 .finish()
1821 }
1822}
1823
1824pub(crate) struct DerivedState<T: Clone + 'static> {
1825 compute: Rc<dyn Fn() -> T>,
1826 pub(crate) state: OwnedMutableState<T>,
1827}
1828
1829impl<T: Clone + 'static> DerivedState<T> {
1830 pub(crate) fn new(runtime: RuntimeHandle, compute: Rc<dyn Fn() -> T>) -> Self {
1831 let initial = compute();
1832 Self {
1833 compute,
1834 state: OwnedMutableState::with_runtime(initial, runtime),
1835 }
1836 }
1837
1838 pub(crate) fn set_compute(&mut self, compute: Rc<dyn Fn() -> T>) {
1839 self.compute = compute;
1840 }
1841
1842 pub(crate) fn recompute(&self) {
1843 let value = (self.compute)();
1844 self.state.set_value(value);
1845 }
1846}
1847
1848impl<T: fmt::Debug + Clone + 'static> fmt::Debug for State<T> {
1849 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1850 if let Some(value) = self.try_value() {
1851 f.debug_struct("State").field("value", &value).finish()
1852 } else {
1853 f.write_str("State { value: <unavailable> }")
1854 }
1855 }
1856}
1857
1858#[cfg(test)]
1859mod tests {
1860 use super::*;
1861
1862 fn create_record_chain(ids: &[SnapshotId]) -> Rc<StateRecord> {
1864 let mut head: Option<Rc<StateRecord>> = None;
1865
1866 for &id in ids.iter().rev() {
1868 head = Some(StateRecord::new(id, 0i32, head));
1869 }
1870
1871 head.expect("create_record_chain called with empty ids")
1872 }
1873
1874 struct ManualState {
1875 head: Rc<StateRecord>,
1876 }
1877
1878 impl ManualState {
1879 fn new(head: Rc<StateRecord>) -> Self {
1880 Self { head }
1881 }
1882 }
1883
1884 impl StateObject for ManualState {
1885 fn object_id(&self) -> ObjectId {
1886 ObjectId(999)
1887 }
1888
1889 fn first_record(&self) -> Rc<StateRecord> {
1890 Rc::clone(&self.head)
1891 }
1892
1893 fn try_readable_record(&self, _: SnapshotId, _: &SnapshotIdSet) -> Option<Rc<StateRecord>> {
1894 Some(Rc::clone(&self.head))
1895 }
1896
1897 fn readable_record(&self, _: SnapshotId, _: &SnapshotIdSet) -> Rc<StateRecord> {
1898 Rc::clone(&self.head)
1899 }
1900
1901 fn prepend_state_record(&self, _: Rc<StateRecord>) {}
1902
1903 fn promote_record(&self, _: SnapshotId) -> Result<(), &'static str> {
1904 Ok(())
1905 }
1906
1907 fn as_any(&self) -> &dyn Any {
1908 self
1909 }
1910 }
1911
1912 fn poison_mutex<T>(mutex: &Mutex<T>) {
1913 let poison_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1914 let _guard = mutex
1915 .lock()
1916 .unwrap_or_else(|poisoned| poisoned.into_inner());
1917 panic!("poison snapshot state mutex for recovery test");
1918 }));
1919
1920 assert!(poison_result.is_err());
1921 }
1922
1923 #[test]
1924 fn snapshot_mutable_state_recovers_poisoned_weak_self_lock() {
1925 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1926
1927 poison_mutex(&state.weak_self);
1928
1929 assert_eq!(state.get(), 100);
1930 assert!(state.set(101));
1931 assert_eq!(state.get(), 101);
1932 }
1933
1934 #[test]
1935 fn snapshot_mutable_state_recovers_poisoned_apply_observer_lock() {
1936 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1937 let calls = Rc::new(Cell::new(0usize));
1938 let observed_calls = Rc::clone(&calls);
1939
1940 poison_mutex(&state.apply_observers);
1941
1942 state.add_apply_observer(Box::new(move || {
1943 observed_calls.set(observed_calls.get() + 1);
1944 }));
1945 state.notify_applied();
1946
1947 assert_eq!(calls.get(), 1);
1948 }
1949
1950 #[test]
1951 fn snapshot_mutable_state_promote_missing_record_returns_error() {
1952 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1953 let missing_snapshot = usize::MAX - 17;
1954
1955 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1956 StateObject::promote_record(&*state, missing_snapshot)
1957 }));
1958
1959 assert!(
1960 matches!(result, Ok(Err("missing child record"))),
1961 "missing child record should be reported through Result, got {result:?}"
1962 );
1963 }
1964
1965 #[test]
1966 fn snapshot_mutable_state_promote_wrong_record_type_returns_error() {
1967 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1968 let child_snapshot = usize::MAX - 31;
1969 let wrong_record = StateRecord::new(child_snapshot, "wrong type", None);
1970 StateObject::prepend_state_record(&*state, wrong_record);
1971
1972 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1973 StateObject::promote_record(&*state, child_snapshot)
1974 }));
1975
1976 assert!(
1977 matches!(result, Ok(Err("child record value missing or wrong type"))),
1978 "wrong child record type should be reported through Result, got {result:?}"
1979 );
1980 }
1981
1982 #[test]
1983 fn snapshot_mutable_state_commit_wrong_record_type_returns_error() {
1984 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1985 let merged = StateRecord::new(usize::MAX - 43, "wrong type", None);
1986
1987 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1988 StateObject::commit_merged_record(&*state, merged)
1989 }));
1990
1991 assert!(
1992 matches!(result, Ok(Err("merged record value missing or wrong type"))),
1993 "wrong merged record type should be reported through Result, got {result:?}"
1994 );
1995 }
1996
1997 #[test]
1998 fn snapshot_mutable_state_merge_wrong_record_type_returns_none() {
1999 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2000 let previous = StateRecord::new(usize::MAX - 51, 1i32, None);
2001 let current = StateRecord::new(usize::MAX - 52, "wrong type", None);
2002 let applied = StateRecord::new(usize::MAX - 53, 2i32, None);
2003
2004 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
2005 StateObject::merge_records(&*state, previous, current, applied)
2006 }));
2007
2008 match result {
2009 Ok(None) => {}
2010 Ok(Some(_)) => panic!("wrong merge record type unexpectedly produced a merged record"),
2011 Err(_) => panic!("wrong merge record type should not panic"),
2012 }
2013 }
2014
2015 #[test]
2016 fn test_used_locked_finds_invalid_snapshot() {
2017 let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, 0i32, None);
2019 let invalid_rec = StateRecord::new(INVALID_SNAPSHOT_ID, 0i32, Some(tail));
2020 let head = StateRecord::new(10, 0i32, Some(invalid_rec.clone()));
2021
2022 let result = used_locked(&head);
2023 assert!(result.is_some());
2024 assert_eq!(result.unwrap().snapshot_id(), INVALID_SNAPSHOT_ID);
2025 }
2026
2027 #[test]
2028 fn test_used_locked_finds_obscured_record() {
2029 crate::snapshot_pinning::reset_pinning_table();
2031
2032 let pin_handle = crate::snapshot_pinning::track_pinning(10, &SnapshotIdSet::EMPTY);
2035
2036 let oldest = StateRecord::new(2, 0i32, None);
2038 let newer = StateRecord::new(5, 0i32, Some(oldest.clone()));
2039 let head = StateRecord::new(100, 0i32, Some(newer));
2040
2041 let result = used_locked(&head);
2042
2043 assert!(result.is_some());
2045 let reused = result.unwrap();
2046 assert_eq!(
2047 reused.snapshot_id(),
2048 2,
2049 "Should return the oldest obscured record"
2050 );
2051
2052 crate::snapshot_pinning::release_pinning(pin_handle);
2054 }
2055
2056 #[test]
2057 fn test_used_locked_no_reusable_record() {
2058 crate::snapshot_pinning::reset_pinning_table();
2060
2061 let high_id = allocate_record_id() + 1000;
2064 let head = create_record_chain(&[high_id, high_id + 1, high_id + 2]);
2065
2066 let result = used_locked(&head);
2067 assert!(
2068 result.is_none(),
2069 "Should find no reusable records when all are recent"
2070 );
2071 }
2072
2073 #[test]
2074 fn test_used_locked_single_old_record() {
2075 crate::snapshot_pinning::reset_pinning_table();
2077
2078 let old = StateRecord::new(2, 0i32, None);
2080 let head = StateRecord::new(100, 0i32, Some(old));
2081
2082 let result = used_locked(&head);
2083 assert!(result.is_none(), "Single old record should not be reused");
2085 }
2086
2087 #[test]
2088 fn test_readable_record_for_preexisting() {
2089 let head = create_record_chain(&[PREEXISTING_SNAPSHOT_ID]);
2090 let invalid = SnapshotIdSet::EMPTY;
2091
2092 let result = readable_record_for(&head, 10, &invalid);
2093 assert!(result.is_some());
2094 assert_eq!(result.unwrap().snapshot_id(), PREEXISTING_SNAPSHOT_ID);
2095 }
2096
2097 #[test]
2098 fn test_readable_record_for_picks_highest_valid() {
2099 let head = create_record_chain(&[10, 5, PREEXISTING_SNAPSHOT_ID]);
2100 let invalid = SnapshotIdSet::EMPTY;
2101
2102 let result = readable_record_for(&head, 10, &invalid);
2104 assert!(result.is_some());
2105 assert_eq!(result.unwrap().snapshot_id(), 10);
2106
2107 let result = readable_record_for(&head, 7, &invalid);
2109 assert!(result.is_some());
2110 assert_eq!(result.unwrap().snapshot_id(), 5);
2111 }
2112
2113 #[test]
2114 fn test_new_overwritable_record_locked_reuses_invalid() {
2115 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2117
2118 let current_head = state.first_record();
2120 let invalid_rec = StateRecord::new(INVALID_SNAPSHOT_ID, 0i32, current_head.next());
2121 current_head.set_next(Some(invalid_rec.clone()));
2122
2123 let result = new_overwritable_record_locked(&*state);
2124
2125 assert!(Rc::ptr_eq(&result, &invalid_rec));
2127 assert_eq!(result.snapshot_id(), SNAPSHOT_ID_MAX);
2128 }
2129
2130 #[test]
2131 fn test_new_overwritable_record_locked_creates_new() {
2132 crate::snapshot_pinning::reset_pinning_table();
2133
2134 let _pin_handle = crate::snapshot_pinning::track_pinning(1, &SnapshotIdSet::EMPTY);
2137
2138 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2140 let old_head = state.first_record();
2141
2142 let result = new_overwritable_record_locked(&*state);
2143
2144 assert_eq!(result.snapshot_id(), SNAPSHOT_ID_MAX);
2146
2147 let new_head = state.first_record();
2149 assert!(
2150 Rc::ptr_eq(&new_head, &result),
2151 "new_head ({:p}) should equal result ({:p})",
2152 Rc::as_ptr(&new_head),
2153 Rc::as_ptr(&result)
2154 );
2155
2156 assert!(result.next().is_some());
2158 assert!(Rc::ptr_eq(&result.next().unwrap(), &old_head));
2159 }
2160
2161 #[test]
2162 fn test_writable_record_reuses_invalid_record() {
2163 crate::snapshot_pinning::reset_pinning_table();
2164
2165 let state = SnapshotMutableState::new_in_arc(7i32, Arc::new(NeverEqual));
2166
2167 let head = state.first_record();
2169 let invalid = StateRecord::new(INVALID_SNAPSHOT_ID, 0i32, head.next());
2170 head.set_next(Some(invalid.clone()));
2171
2172 let snapshot_id = allocate_record_id();
2173 let result = state.writable_record(snapshot_id, &SnapshotIdSet::EMPTY);
2174
2175 assert!(
2176 Rc::ptr_eq(&result, &invalid),
2177 "Expected writable_record to reuse the INVALID record"
2178 );
2179 assert_eq!(result.snapshot_id(), snapshot_id);
2180 result.with_value(|value: &i32| {
2181 assert_eq!(*value, 7, "Reused record should copy the readable value");
2182 });
2183 assert!(!result.is_tombstone());
2184 }
2185
2186 #[test]
2187 fn test_writable_record_creates_new_when_reuse_disallowed() {
2188 crate::snapshot_pinning::reset_pinning_table();
2189 let pin = crate::snapshot_pinning::track_pinning(1, &SnapshotIdSet::EMPTY);
2190
2191 let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2192 let original_head = state.first_record();
2193 let preexisting = original_head
2194 .next()
2195 .expect("preexisting record should exist for newly created state");
2196
2197 let snapshot_id = allocate_record_id();
2198 let result = state.writable_record(snapshot_id, &SnapshotIdSet::EMPTY);
2199
2200 assert!(
2201 !Rc::ptr_eq(&result, &original_head),
2202 "Should not reuse the current head when reuse is disallowed"
2203 );
2204 assert!(
2205 !Rc::ptr_eq(&result, &preexisting),
2206 "Should not reuse the PREEXISTING record"
2207 );
2208 assert_eq!(result.snapshot_id(), snapshot_id);
2209 result.with_value(|value: &i32| assert_eq!(*value, 42));
2210
2211 let new_head = state.first_record();
2212 assert!(
2213 Rc::ptr_eq(&new_head, &result),
2214 "Newly created record should become the head of the chain"
2215 );
2216
2217 crate::snapshot_pinning::release_pinning(pin);
2218 }
2219
2220 #[test]
2221 fn test_state_record_clear_for_reuse() {
2222 let record = StateRecord::new(10, 42i32, None);
2223
2224 record.with_value(|val: &i32| {
2226 assert_eq!(*val, 42);
2227 });
2228
2229 record.clear_for_reuse();
2231
2232 assert_eq!(record.snapshot_id(), 10);
2235 }
2236
2237 #[test]
2238 fn test_overwrite_unused_records_no_old_records() {
2239 crate::snapshot_pinning::reset_pinning_table();
2240
2241 let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2243
2244 let _pin = crate::snapshot_pinning::track_pinning(1, &SnapshotIdSet::EMPTY);
2247
2248 let should_retain = state.overwrite_unused_records();
2249
2250 assert!(
2252 should_retain,
2253 "Should retain multiple records when none are old enough"
2254 );
2255
2256 let mut cursor = Some(state.first_record());
2258 while let Some(record) = cursor {
2259 assert_ne!(record.snapshot_id(), INVALID_SNAPSHOT_ID);
2260 cursor = record.next();
2261 }
2262 }
2263
2264 #[test]
2265 fn test_overwrite_unused_records_basic_cleanup() {
2266 crate::snapshot_pinning::reset_pinning_table();
2268
2269 let rec1 = StateRecord::new(100, 1i32, None);
2271 let rec2 = StateRecord::new(200, 2i32, Some(rec1.clone()));
2272 let rec3 = StateRecord::new(300, 3i32, Some(rec2.clone()));
2273
2274 struct TestState {
2276 head: Rc<StateRecord>,
2277 }
2278 impl StateObject for TestState {
2279 fn object_id(&self) -> ObjectId {
2280 ObjectId(999)
2281 }
2282 fn first_record(&self) -> Rc<StateRecord> {
2283 Rc::clone(&self.head)
2284 }
2285 fn try_readable_record(
2286 &self,
2287 _: SnapshotId,
2288 _: &SnapshotIdSet,
2289 ) -> Option<Rc<StateRecord>> {
2290 Some(Rc::clone(&self.head))
2291 }
2292 fn readable_record(&self, _: SnapshotId, _: &SnapshotIdSet) -> Rc<StateRecord> {
2293 Rc::clone(&self.head)
2294 }
2295 fn prepend_state_record(&self, _: Rc<StateRecord>) {}
2296 fn promote_record(&self, _: SnapshotId) -> Result<(), &'static str> {
2297 Ok(())
2298 }
2299 fn as_any(&self) -> &dyn Any {
2300 self
2301 }
2302 }
2303
2304 let test_state = TestState { head: rec3.clone() };
2305
2306 let _pin = crate::snapshot_pinning::track_pinning(1000, &SnapshotIdSet::EMPTY);
2308
2309 let result = overwrite_unused_records_locked::<i32>(&test_state);
2310
2311 assert_eq!(rec3.snapshot_id(), 300);
2313 assert_eq!(rec2.snapshot_id(), INVALID_SNAPSHOT_ID);
2314 assert_eq!(rec1.snapshot_id(), INVALID_SNAPSHOT_ID);
2315
2316 assert!(!result);
2318 }
2319
2320 #[test]
2321 fn test_overwrite_unused_records_single_record_only() {
2322 crate::snapshot_pinning::reset_pinning_table();
2323
2324 let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2325
2326 let head = state.first_record();
2328 head.set_next(None);
2329
2330 let should_retain = state.overwrite_unused_records();
2331
2332 assert!(!should_retain, "Single record should return false");
2334 }
2335
2336 #[test]
2337 fn snapshot_state_try_get_reports_missing_visible_record_without_panicking() {
2338 crate::snapshot_pinning::reset_pinning_table();
2339
2340 let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2341 let head = state.first_record();
2342 head.set_snapshot_id(SNAPSHOT_ID_MAX);
2343 head.set_next(None);
2344
2345 assert_eq!(state.try_get(), None);
2346 }
2347
2348 #[test]
2349 fn test_overwrite_unused_records_clears_values() {
2350 crate::snapshot_pinning::reset_pinning_table();
2351
2352 let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, 0i32, None);
2353 let old_rec1 = StateRecord::new(2, 999i32, Some(tail.clone()));
2354 let old_rec2 = StateRecord::new(3, 888i32, Some(old_rec1.clone()));
2355 let head = StateRecord::new(150, 42i32, Some(old_rec2.clone()));
2356 let state = ManualState::new(head.clone());
2357
2358 old_rec1.with_value(|val: &i32| {
2360 assert_eq!(*val, 999);
2361 });
2362
2363 let _pin = crate::snapshot_pinning::track_pinning(100, &SnapshotIdSet::EMPTY);
2364 overwrite_unused_records_locked::<i32>(&state);
2365
2366 assert_eq!(old_rec1.snapshot_id(), INVALID_SNAPSHOT_ID);
2368 }
2370
2371 #[test]
2372 fn test_overwrite_unused_records_mixed_old_and_new() {
2373 crate::snapshot_pinning::reset_pinning_table();
2374
2375 let preexisting = StateRecord::new(PREEXISTING_SNAPSHOT_ID, 0i32, None);
2377 let rec2 = StateRecord::new(2, 100i32, Some(preexisting.clone()));
2378 let rec5 = StateRecord::new(5, 100i32, Some(rec2.clone()));
2379 let rec50 = StateRecord::new(50, 100i32, Some(rec5.clone()));
2380 let head = StateRecord::new(120, 100i32, Some(rec50.clone()));
2381 let state = ManualState::new(head.clone());
2382
2383 let _pin = crate::snapshot_pinning::track_pinning(40, &SnapshotIdSet::EMPTY);
2385
2386 let should_retain = overwrite_unused_records_locked::<i32>(&state);
2387 assert!(should_retain);
2388
2389 assert_eq!(rec50.snapshot_id(), 50);
2391 assert_eq!(rec5.snapshot_id(), 5);
2393 assert_eq!(rec2.snapshot_id(), INVALID_SNAPSHOT_ID);
2395 }
2396
2397 #[test]
2398 fn test_readable_record_for_skips_invalid_set() {
2399 let head = create_record_chain(&[10, 5, PREEXISTING_SNAPSHOT_ID]);
2400 let invalid = SnapshotIdSet::new().set(5);
2401
2402 let result = readable_record_for(&head, 10, &invalid);
2404 assert!(result.is_some());
2405 assert_eq!(result.unwrap().snapshot_id(), 10);
2406
2407 let result = readable_record_for(&head, 7, &invalid);
2409 assert!(result.is_some());
2410 assert_eq!(result.unwrap().snapshot_id(), PREEXISTING_SNAPSHOT_ID);
2411 }
2412
2413 #[test]
2416 fn test_assign_value_copies_int() {
2417 let source = StateRecord::new(10, 42i32, None);
2418 let target = StateRecord::new(20, 0i32, None);
2419
2420 target.assign_value::<i32>(&source).expect("copy int value");
2421
2422 target.with_value(|val: &i32| {
2424 assert_eq!(*val, 42);
2425 });
2426
2427 source.with_value(|val: &i32| {
2429 assert_eq!(*val, 42);
2430 });
2431
2432 assert_eq!(source.snapshot_id(), 10);
2434 assert_eq!(target.snapshot_id(), 20);
2435 }
2436
2437 #[test]
2438 fn test_assign_value_copies_string() {
2439 let source = StateRecord::new(10, "hello".to_string(), None);
2440 let target = StateRecord::new(20, "world".to_string(), None);
2441
2442 target
2443 .assign_value::<String>(&source)
2444 .expect("copy string value");
2445
2446 target.with_value(|val: &String| {
2448 assert_eq!(val, "hello");
2449 });
2450
2451 source.with_value(|val: &String| {
2453 assert_eq!(val, "hello");
2454 });
2455 }
2456
2457 #[test]
2458 fn test_assign_value_reports_cleared_source() {
2459 let source = StateRecord::new(10, 42i32, None);
2460 let target = StateRecord::new(20, 0i32, None);
2461
2462 source.clear_value();
2463
2464 assert_eq!(
2465 target.assign_value::<i32>(&source),
2466 Err(StateRecordValueError::MissingOrWrongType {
2467 expected: std::any::type_name::<i32>(),
2468 })
2469 );
2470 assert_eq!(target.with_value(|val: &i32| *val), 0);
2471 }
2472
2473 #[test]
2474 fn test_assign_value_overwrites_existing_value() {
2475 let source = StateRecord::new(10, 100i32, None);
2476 let target = StateRecord::new(20, 999i32, None);
2477
2478 target.with_value(|val: &i32| {
2480 assert_eq!(*val, 999);
2481 });
2482
2483 target
2485 .assign_value::<i32>(&source)
2486 .expect("overwrite int value");
2487
2488 target.with_value(|val: &i32| {
2490 assert_eq!(*val, 100);
2491 });
2492 }
2493
2494 #[test]
2495 fn test_assign_value_with_custom_type() {
2496 #[derive(Clone, PartialEq, Debug)]
2497 struct Point {
2498 x: f64,
2499 y: f64,
2500 }
2501
2502 let source = StateRecord::new(10, Point { x: 1.5, y: 2.5 }, None);
2503 let target = StateRecord::new(20, Point { x: 0.0, y: 0.0 }, None);
2504
2505 target
2506 .assign_value::<Point>(&source)
2507 .expect("copy point value");
2508
2509 target.with_value(|val: &Point| {
2510 assert_eq!(val, &Point { x: 1.5, y: 2.5 });
2511 });
2512 }
2513
2514 #[test]
2515 fn test_assign_value_self_assignment() {
2516 let record = StateRecord::new(10, 42i32, None);
2517
2518 record
2520 .assign_value::<i32>(&record)
2521 .expect("self-assign int value");
2522
2523 record.with_value(|val: &i32| {
2524 assert_eq!(*val, 42);
2525 });
2526 }
2527
2528 #[test]
2529 fn test_assign_value_with_vec() {
2530 let source = StateRecord::new(10, vec![1, 2, 3, 4, 5], None);
2531 let target = StateRecord::new(20, Vec::<i32>::new(), None);
2532
2533 target
2534 .assign_value::<Vec<i32>>(&source)
2535 .expect("copy vec value");
2536
2537 target.with_value(|val: &Vec<i32>| {
2538 assert_eq!(val, &vec![1, 2, 3, 4, 5]);
2539 });
2540
2541 source.replace_value(vec![10, 20]);
2543 target.with_value(|val: &Vec<i32>| {
2544 assert_eq!(val, &vec![1, 2, 3, 4, 5]);
2545 });
2546 }
2547}