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