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 trait StateObject: Any {
556 fn object_id(&self) -> ObjectId;
557 fn first_record(&self) -> Rc<StateRecord>;
558 fn try_readable_record(
559 &self,
560 snapshot_id: SnapshotId,
561 invalid: &SnapshotIdSet,
562 ) -> Option<Rc<StateRecord>>;
563 fn readable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord>;
564
565 fn prepend_state_record(&self, record: Rc<StateRecord>);
569
570 fn merge_records(
571 &self,
572 _previous: Rc<StateRecord>,
573 _current: Rc<StateRecord>,
574 _applied: Rc<StateRecord>,
575 ) -> Option<Rc<StateRecord>> {
576 None
577 }
578
579 fn commit_merged_record(&self, _merged: Rc<StateRecord>) -> Result<SnapshotId, &'static str> {
580 Err("StateObject does not support merged record commits")
581 }
582 fn promote_record(&self, child_id: SnapshotId) -> Result<(), &'static str>;
583
584 fn overwrite_unused_records(&self) -> bool {
589 false }
591
592 fn as_any(&self) -> &dyn Any;
594}
595
596pub(crate) struct SnapshotMutableState<T> {
597 head: CurrentRecord,
598 policy: Arc<dyn MutationPolicy<T>>,
599 id: ObjectId,
600 weak_self: Mutex<Option<Weak<Self>>>,
601 apply_observers: Mutex<Vec<Box<dyn Fn() + 'static>>>,
602}
603
604impl<T> SnapshotMutableState<T> {
605 fn assert_chain_integrity(&self, caller: &str, snapshot_context: Option<SnapshotId>) {
606 if !should_check_chain_integrity() {
607 return;
608 }
609 let head = self.head.clone_head();
610 let mut cursor = Some(head);
611 let mut seen: HashSet<usize> = HashSet::default();
612 let mut ids = Vec::new();
613
614 while let Some(record) = cursor {
615 let addr = Rc::as_ptr(&record) as usize;
616 assert!(
617 seen.insert(addr),
618 "SnapshotMutableState::{} detected duplicate/cycle at record {:p} for state {:?} (snapshot_context={:?}, chain_ids={:?})",
619 caller,
620 Rc::as_ptr(&record),
621 self.id,
622 snapshot_context,
623 ids
624 );
625 ids.push(record.snapshot_id());
626 cursor = record.next();
627 }
628
629 assert!(
630 !ids.is_empty(),
631 "SnapshotMutableState::{} finished integrity scan with empty id list for state {:?} (snapshot_context={:?})",
632 caller,
633 self.id,
634 snapshot_context
635 );
636 }
637}
638
639fn should_check_chain_integrity() -> bool {
640 #[cfg(debug_assertions)]
641 {
642 true
643 }
644
645 #[cfg(not(debug_assertions))]
646 {
647 std::env::var_os("CRANPOSE_ASSERT_STATE_CHAIN").is_some()
648 }
649}
650
651impl<T: Clone + 'static> SnapshotMutableState<T> {
652 fn record_chain_debug(&self) -> Vec<(SnapshotId, bool)> {
653 let mut chain_ids = Vec::new();
654 let mut cursor = Some(self.first_record());
655 while let Some(record) = cursor {
656 chain_ids.push((record.snapshot_id(), record.is_tombstone()));
657 cursor = record.next();
658 }
659 chain_ids
660 }
661
662 fn readable_record_for_active_snapshot(&self) -> Result<Rc<StateRecord>, StateReadFailure> {
663 let snapshot = active_snapshot();
664 if let Some(state) = self.upgrade_self() {
665 snapshot.record_read(&*state);
666 }
667
668 let snapshot_id = snapshot.snapshot_id();
669 let invalid = snapshot.invalid();
670
671 if let Some(record) = self.readable_for(snapshot_id, &invalid) {
672 return Ok(record);
673 }
674
675 let fresh_snapshot = active_snapshot();
676 let fresh_id = fresh_snapshot.snapshot_id();
677 let fresh_invalid = fresh_snapshot.invalid();
678
679 if let Some(record) = self.readable_for(fresh_id, &fresh_invalid) {
680 return Ok(record);
681 }
682
683 let global = GlobalSnapshot::get_or_create();
684 let global_id = global.snapshot_id();
685 let global_invalid = global.invalid();
686
687 if let Some(record) = self.readable_for(global_id, &global_invalid) {
688 return Ok(record);
689 }
690
691 Err(StateReadFailure {
692 state_id: self.id,
693 snapshot_id,
694 fresh_snapshot_id: fresh_id,
695 fresh_invalid,
696 record_chain: self.record_chain_debug(),
697 })
698 }
699
700 fn readable_for(
701 &self,
702 snapshot_id: SnapshotId,
703 invalid: &SnapshotIdSet,
704 ) -> Option<Rc<StateRecord>> {
705 let head = self.first_record();
706 readable_record_for(&head, snapshot_id, invalid)
707 }
708
709 fn writable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord> {
710 let readable = match self.readable_for(snapshot_id, invalid) {
711 Some(record) => record,
712 None => {
713 let current_head = self.head.clone_head();
714 let refreshed = readable_record_for(¤t_head, snapshot_id, invalid);
715 let source = refreshed.unwrap_or_else(|| current_head.clone());
716
717 let cloned_value = source.with_value(|value: &T| value.clone());
721 let new_head = StateRecord::new(snapshot_id, cloned_value, Some(current_head));
722 self.head.replace(new_head.clone());
723 self.assert_chain_integrity("writable_record(recover)", Some(snapshot_id));
724 return new_head;
725 }
726 };
727
728 if readable.snapshot_id() == snapshot_id {
729 return readable;
730 }
731
732 let refreshed = {
733 let current_head = self.head.clone_head();
734 let refreshed = readable_record_for(¤t_head, snapshot_id, invalid).unwrap_or_else(
735 || {
736 panic!(
737 "SnapshotMutableState::writable_record failed to locate refreshed readable record (state {:?}, snapshot_id={}, invalid={:?})",
738 self.id, snapshot_id, invalid
739 )
740 },
741 );
742
743 if refreshed.snapshot_id() == snapshot_id {
744 return refreshed;
745 }
746
747 Rc::clone(&refreshed)
748 };
749
750 let overwritable = new_overwritable_record_locked(self);
751 if let Err(error) = overwritable.assign_value::<T>(&refreshed) {
752 log::error!(
753 "snapshot writable record could not copy refreshed value for state {:?}: {:?}",
754 self.id,
755 error
756 );
757 }
758 overwritable.set_snapshot_id(snapshot_id);
759 overwritable.set_tombstone(false);
760
761 self.assert_chain_integrity("writable_record(reuse)", Some(snapshot_id));
762
763 overwritable
764 }
765
766 pub(crate) fn new_in_arc(initial: T, policy: Arc<dyn MutationPolicy<T>>) -> Arc<Self> {
767 let snapshot = active_snapshot();
768 let snapshot_id = snapshot.snapshot_id();
769
770 let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, initial.clone(), None);
771 let head = StateRecord::new(snapshot_id, initial, Some(tail));
772
773 let mut state = Arc::new(Self {
774 head: CurrentRecord::new(head),
775 policy,
776 id: ObjectId::default(),
777 weak_self: Mutex::new(None),
778 apply_observers: Mutex::new(Vec::new()),
779 });
780
781 let id = ObjectId::new(&state);
782 if let Some(state_inner) = Arc::get_mut(&mut state) {
783 state_inner.id = id;
784 }
785
786 *state.lock_weak_self() = Some(Arc::downgrade(&state));
787
788 state
791 }
792
793 pub(crate) fn add_apply_observer(&self, observer: Box<dyn Fn() + 'static>) {
794 self.lock_apply_observers().push(observer);
795 }
796
797 fn notify_applied(&self) {
798 let observers = self.lock_apply_observers();
799 for observer in observers.iter() {
800 observer();
801 }
802 }
803
804 fn lock_weak_self(&self) -> MutexGuard<'_, Option<Weak<Self>>> {
805 self.weak_self
806 .lock()
807 .unwrap_or_else(|poisoned| poisoned.into_inner())
808 }
809
810 fn lock_apply_observers(&self) -> MutexGuard<'_, Vec<Box<dyn Fn() + 'static>>> {
811 self.apply_observers
812 .lock()
813 .unwrap_or_else(|poisoned| poisoned.into_inner())
814 }
815
816 fn upgrade_self(&self) -> Option<Arc<Self>> {
817 self.lock_weak_self()
818 .as_ref()
819 .and_then(|weak| weak.upgrade())
820 }
821
822 #[inline]
823 pub(crate) fn id(&self) -> ObjectId {
824 self.id
825 }
826
827 pub(crate) fn try_with_value<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
828 let record = self.readable_record_for_active_snapshot().ok()?;
829 record.try_with_value(f)
830 }
831
832 pub(crate) fn try_get(&self) -> Option<T> {
833 self.try_with_value(Clone::clone)
834 }
835
836 pub(crate) fn get(&self) -> T {
837 let record = self
838 .readable_record_for_active_snapshot()
839 .unwrap_or_else(|failure| panic!("{failure}"));
840 record.with_value(|value: &T| value.clone())
841 }
842
843 pub(crate) fn set(&self, new_value: T) -> bool {
844 #[cfg(debug_assertions)]
846 {
847 let in_handler = crate::in_event_handler();
848 let in_snapshot = crate::in_applied_snapshot();
849 if in_handler && !in_snapshot {
850 log::warn!(
851 target: "cranpose::state",
852 "State modified in event handler without run_in_mutable_snapshot; \
853 this can make updates invisible to other contexts. Wrap the handler \
854 in run_in_mutable_snapshot() or dispatch_ui_event(). State: {:?}",
855 self.id
856 );
857 }
858 }
859
860 let snapshot = active_snapshot();
861 let snapshot_id = snapshot.snapshot_id();
862
863 match &snapshot {
864 AnySnapshot::Global(global) => {
865 let invalid = snapshot.invalid();
866 let equivalent = self
867 .readable_for(snapshot_id, &invalid)
868 .map(|record| {
869 record.with_value(|current: &T| self.policy.equivalent(current, &new_value))
870 })
871 .unwrap_or(false);
872 if equivalent {
873 return false;
874 }
875
876 if global.has_pending_children() {
877 panic!(
878 "SnapshotMutableState::set attempted global write while pending children {:?} exist (state {:?}, snapshot_id={})",
879 global.pending_children(),
880 self.id,
881 snapshot_id
882 );
883 }
884
885 let mut written_state: Option<Arc<dyn StateObject>> = None;
886 if let Some(state) = self.upgrade_self() {
887 let trait_object: Arc<dyn StateObject> = state.clone();
888 snapshot.record_write(trait_object.clone());
889 written_state = Some(trait_object);
890 }
891 mark_update_write(self.id);
892
893 let new_id = allocate_record_id();
894 let record = new_overwritable_record_as_head_locked(self);
895 record.replace_value(new_value);
896 record.set_snapshot_id(new_id);
897 record.set_tombstone(false);
898 advance_global_snapshot(new_id);
899 self.assert_chain_integrity("set(global-push)", Some(snapshot_id));
900
901 if !global.has_pending_children() {
902 let mut cursor = record.next();
903 while let Some(node) = cursor {
904 if !node.is_tombstone() && node.snapshot_id() != PREEXISTING_SNAPSHOT_ID {
905 node.clear_value();
906 node.set_tombstone(true);
907 }
908 cursor = node.next();
909 }
910 self.assert_chain_integrity("set(global-tombstone)", Some(snapshot_id));
911 }
912
913 if let Some(modified) = written_state.as_ref() {
914 crate::snapshot_v2::notify_apply_observers(
915 std::slice::from_ref(modified),
916 new_id,
917 );
918 }
919 }
920 AnySnapshot::Mutable(_)
921 | AnySnapshot::NestedMutable(_)
922 | AnySnapshot::TransparentMutable(_) => {
923 let invalid = snapshot.invalid();
924 let equivalent = self
925 .readable_for(snapshot_id, &invalid)
926 .map(|record| {
927 record.with_value(|current: &T| self.policy.equivalent(current, &new_value))
928 })
929 .unwrap_or(false);
930 if equivalent {
931 return false;
932 }
933
934 if let Some(state) = self.upgrade_self() {
935 let trait_object: Arc<dyn StateObject> = state.clone();
936 snapshot.record_write(trait_object);
937 }
938 mark_update_write(self.id);
939
940 let record = self.writable_record(snapshot_id, &invalid);
941 record.replace_value(new_value);
942 self.assert_chain_integrity("set(child-writable)", Some(snapshot_id));
943 }
944 AnySnapshot::Readonly(_)
945 | AnySnapshot::NestedReadonly(_)
946 | AnySnapshot::TransparentReadonly(_) => {
947 panic!("Cannot write to a read-only snapshot");
948 }
949 }
950
951 true
956 }
957}
958
959thread_local! {
960 static ACTIVE_UPDATES: RefCell<HashSet<ObjectId>> = RefCell::new(HashSet::default());
961 static PENDING_WRITES: RefCell<HashSet<ObjectId>> = RefCell::new(HashSet::default());
962}
963
964pub(crate) struct UpdateScope {
965 id: ObjectId,
966 finished: bool,
967}
968
969impl UpdateScope {
970 pub(crate) fn new(id: ObjectId) -> Self {
971 ACTIVE_UPDATES.with(|active| {
972 active.borrow_mut().insert(id);
973 });
974 PENDING_WRITES.with(|pending| {
975 pending.borrow_mut().remove(&id);
976 });
977 Self {
978 id,
979 finished: false,
980 }
981 }
982
983 pub(crate) fn finish(mut self) -> bool {
984 self.finished = true;
985 ACTIVE_UPDATES.with(|active| {
986 active.borrow_mut().remove(&self.id);
987 });
988 PENDING_WRITES.with(|pending| pending.borrow_mut().remove(&self.id))
989 }
990}
991
992impl Drop for UpdateScope {
993 fn drop(&mut self) {
994 if self.finished {
995 return;
996 }
997 ACTIVE_UPDATES.with(|active| {
998 active.borrow_mut().remove(&self.id);
999 });
1000 PENDING_WRITES.with(|pending| {
1001 pending.borrow_mut().remove(&self.id);
1002 });
1003 }
1004}
1005
1006fn mark_update_write(id: ObjectId) {
1007 ACTIVE_UPDATES.with(|active| {
1008 if active.borrow().contains(&id) {
1009 PENDING_WRITES.with(|pending| {
1010 pending.borrow_mut().insert(id);
1011 });
1012 }
1013 });
1014}
1015
1016impl<T: Clone + 'static> SnapshotMutableState<T> {
1017 fn try_readable_record(
1019 &self,
1020 snapshot_id: SnapshotId,
1021 invalid: &SnapshotIdSet,
1022 ) -> Option<Rc<StateRecord>> {
1023 self.readable_for(snapshot_id, invalid)
1024 }
1025}
1026
1027impl<T: Clone + 'static> StateObject for SnapshotMutableState<T> {
1028 fn object_id(&self) -> ObjectId {
1029 self.id
1030 }
1031
1032 fn first_record(&self) -> Rc<StateRecord> {
1033 self.head.clone_head()
1034 }
1035
1036 fn try_readable_record(
1037 &self,
1038 snapshot_id: SnapshotId,
1039 invalid: &SnapshotIdSet,
1040 ) -> Option<Rc<StateRecord>> {
1041 self.try_readable_record(snapshot_id, invalid)
1042 }
1043
1044 fn readable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord> {
1045 self.try_readable_record(snapshot_id, invalid)
1046 .unwrap_or_else(|| {
1047 panic!(
1048 "SnapshotMutableState::readable_record returned null (state={:?}, snapshot_id={})",
1049 self.id, snapshot_id
1050 )
1051 })
1052 }
1053
1054 fn prepend_state_record(&self, record: Rc<StateRecord>) {
1055 self.head.prepend(record);
1056 }
1057
1058 fn merge_records(
1059 &self,
1060 previous: Rc<StateRecord>,
1061 current: Rc<StateRecord>,
1062 applied: Rc<StateRecord>,
1063 ) -> Option<Rc<StateRecord>> {
1064 let Some(current_value) = current.try_with_value(|value: &T| value.clone()) else {
1065 log::error!(
1066 "SnapshotMutableState::merge_records current record value missing or wrong type (state {:?}, current_id={})",
1067 self.id,
1068 current.snapshot_id()
1069 );
1070 return None;
1071 };
1072 let Some(applied_value) = applied.try_with_value(|value: &T| value.clone()) else {
1073 log::error!(
1074 "SnapshotMutableState::merge_records applied record value missing or wrong type (state {:?}, applied_id={})",
1075 self.id,
1076 applied.snapshot_id()
1077 );
1078 return None;
1079 };
1080 if self.policy.equivalent(¤t_value, &applied_value) {
1081 return Some(current);
1082 }
1083
1084 let Some(previous_value) = previous.try_with_value(|value: &T| value.clone()) else {
1085 log::error!(
1086 "SnapshotMutableState::merge_records previous record value missing or wrong type (state {:?}, previous_id={})",
1087 self.id,
1088 previous.snapshot_id()
1089 );
1090 return None;
1091 };
1092 let merged = self
1093 .policy
1094 .merge(&previous_value, ¤t_value, &applied_value)?;
1095
1096 Some(StateRecord::new(applied.snapshot_id(), merged, None))
1097 }
1098
1099 fn promote_record(&self, child_id: SnapshotId) -> Result<(), &'static str> {
1100 let head = self.first_record();
1101 let mut cursor = Some(head);
1102 while let Some(record) = cursor {
1103 if record.snapshot_id() == child_id {
1104 let Some(cloned) = record.try_with_value(|value: &T| value.clone()) else {
1105 log::error!(
1106 "SnapshotMutableState::promote_record child record value missing or wrong type (state {:?}, child_id={})",
1107 self.id,
1108 child_id
1109 );
1110 return Err("child record value missing or wrong type");
1111 };
1112 let new_id = allocate_record_id();
1119 let promoted = new_overwritable_record_as_head_locked(self);
1120 promoted.replace_value(cloned);
1121 promoted.set_tombstone(false);
1122 promoted.set_snapshot_id(new_id);
1123 advance_global_snapshot(new_id);
1124 self.notify_applied();
1125 self.assert_chain_integrity("promote_record", Some(child_id));
1126 return Ok(());
1127 }
1128 cursor = record.next();
1129 }
1130 log::error!(
1131 "SnapshotMutableState::promote_record missing child record (state {:?}, child_id={})",
1132 self.id,
1133 child_id
1134 );
1135 Err("missing child record")
1136 }
1137
1138 fn commit_merged_record(&self, merged: Rc<StateRecord>) -> Result<SnapshotId, &'static str> {
1139 let Some(value) = merged.try_with_value(|value: &T| value.clone()) else {
1140 log::error!(
1141 "SnapshotMutableState::commit_merged_record merged record value missing or wrong type (state {:?}, merged_id={})",
1142 self.id,
1143 merged.snapshot_id()
1144 );
1145 return Err("merged record value missing or wrong type");
1146 };
1147 let new_id = allocate_record_id();
1150 let committed = new_overwritable_record_as_head_locked(self);
1151 committed.replace_value(value);
1152 committed.set_tombstone(false);
1153 committed.set_snapshot_id(new_id);
1154 advance_global_snapshot(new_id);
1155 self.notify_applied();
1156 self.assert_chain_integrity("commit_merged_record", Some(new_id));
1157 Ok(new_id)
1158 }
1159
1160 fn overwrite_unused_records(&self) -> bool {
1161 overwrite_unused_records_locked::<T>(self)
1162 }
1163
1164 fn as_any(&self) -> &dyn Any {
1165 self
1166 }
1167}
1168
1169pub(crate) struct MutableStateInner<T: Clone + 'static> {
1170 pub(crate) state: Arc<SnapshotMutableState<T>>,
1171 pub(crate) watchers: RefCell<HashMap<ScopeId, RcWeak<RecomposeScopeInner>>>,
1172 runtime: RuntimeHandle,
1173 state_id: Cell<Option<StateId>>,
1174}
1175
1176fn shrink_watchers_if_sparse(watchers: &mut HashMap<ScopeId, RcWeak<RecomposeScopeInner>>) {
1177 let len = watchers.len();
1178 let capacity = watchers.capacity();
1179 if capacity > len.saturating_mul(4).max(32) {
1180 watchers.shrink_to_fit();
1181 }
1182}
1183
1184impl<T: Clone + 'static> MutableStateInner<T> {
1185 pub(crate) fn new_with_policy(
1186 value: T,
1187 runtime: RuntimeHandle,
1188 policy: Arc<dyn MutationPolicy<T>>,
1189 ) -> Self {
1190 Self {
1191 state: SnapshotMutableState::new_in_arc(value, policy),
1192 watchers: RefCell::new(HashMap::default()),
1193 runtime,
1194 state_id: Cell::new(None),
1195 }
1196 }
1197
1198 pub(crate) fn install_snapshot_observer(&self, state_id: StateId) {
1199 self.state_id.set(Some(state_id));
1200 let runtime_handle = self.runtime.clone();
1201 self.state.add_apply_observer(Box::new(move || {
1202 let runtime = runtime_handle.clone();
1203 runtime_handle.enqueue_ui_task(Box::new(move || {
1204 runtime.with_state_arena(|arena| {
1205 let _ = arena.with_typed_opt::<T, _>(state_id, |inner| {
1206 inner.invalidate_watchers();
1207 });
1208 });
1209 }));
1210 }));
1211 }
1212
1213 fn with_value<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1214 let value = self.state.get();
1215 f(&value)
1216 }
1217
1218 fn register_scope(&self, scope: &RecomposeScope) -> bool {
1219 let mut watchers = self.watchers.borrow_mut();
1220 match watchers.get(&scope.id()) {
1221 Some(existing) if existing.upgrade().is_some() => false,
1222 _ => {
1223 watchers.insert(scope.id(), scope.downgrade());
1224 true
1225 }
1226 }
1227 }
1228
1229 pub(crate) fn unregister_scope(&self, scope_id: ScopeId) {
1230 let mut watchers = self.watchers.borrow_mut();
1231 if watchers
1236 .get(&scope_id)
1237 .is_some_and(|weak| weak.upgrade().is_none())
1238 {
1239 watchers.remove(&scope_id);
1240 shrink_watchers_if_sparse(&mut watchers);
1241 }
1242 }
1243
1244 fn state_id(&self) -> Option<StateId> {
1245 self.state_id.get()
1246 }
1247
1248 fn invalidate_watchers(&self) {
1249 let watchers: Vec<RecomposeScope> = {
1250 let mut watchers = self.watchers.borrow_mut();
1251 let mut live = Vec::with_capacity(watchers.len());
1252 watchers.retain(|_, scope| {
1253 if let Some(inner) = scope.upgrade() {
1254 live.push(RecomposeScope { inner });
1255 true
1256 } else {
1257 false
1258 }
1259 });
1260 shrink_watchers_if_sparse(&mut watchers);
1261 live
1262 };
1263
1264 for watcher in watchers {
1265 debug_record_scope_invalidation::<T>(watcher.id(), self.state_id.get());
1266 if let Some(state_id) = self.state_id.get() {
1267 watcher.invalidate_from_state(state_id);
1268 } else {
1269 watcher.invalidate();
1270 }
1271 }
1272 }
1273}
1274
1275fn register_current_state_scope<T: Clone + 'static>(inner: &MutableStateInner<T>) {
1276 let Some(Some(scope)) =
1277 with_current_composer_opt(|composer| composer.current_state_invalidation_scope())
1278 else {
1279 return;
1280 };
1281 if inner.register_scope(&scope) {
1282 if let Some(state_id) = inner.state_id() {
1283 scope.record_state_subscription(state_id);
1284 }
1285 }
1286}
1287
1288pub struct State<T: Clone + 'static> {
1290 id: StateId,
1291 runtime_id: runtime::RuntimeId,
1292 _marker: PhantomData<fn() -> T>,
1293}
1294
1295pub struct MutableState<T: Clone + 'static> {
1301 id: StateId,
1302 runtime_id: runtime::RuntimeId,
1303 _marker: PhantomData<fn() -> T>,
1304}
1305
1306#[derive(Clone)]
1308pub struct OwnedMutableState<T: Clone + 'static> {
1309 state: MutableState<T>,
1310 _lease: Rc<runtime::StateHandleLease>,
1311 _marker: PhantomData<fn() -> T>,
1312}
1313
1314impl<T: Clone + 'static> PartialEq for State<T> {
1315 fn eq(&self, other: &Self) -> bool {
1316 self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
1317 }
1318}
1319
1320impl<T: Clone + 'static> Eq for State<T> {}
1321
1322impl<T: Clone + 'static> PartialEq for MutableState<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 MutableState<T> {}
1329
1330impl<T: Clone + 'static> Copy for State<T> {}
1331
1332impl<T: Clone + 'static> Clone for State<T> {
1333 fn clone(&self) -> Self {
1334 *self
1335 }
1336}
1337
1338impl<T: Clone + 'static> Copy for MutableState<T> {}
1339
1340impl<T: Clone + 'static> Clone for MutableState<T> {
1341 fn clone(&self) -> Self {
1342 *self
1343 }
1344}
1345
1346impl<T: Clone + 'static> State<T> {
1347 fn state_id(&self) -> StateId {
1348 self.id
1349 }
1350
1351 fn runtime_id(&self) -> runtime::RuntimeId {
1352 self.runtime_id
1353 }
1354
1355 fn runtime_handle(&self) -> RuntimeHandle {
1356 runtime::runtime_handle_by_id(self.runtime_id())
1357 .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
1358 }
1359
1360 fn runtime_handle_opt(&self) -> Option<RuntimeHandle> {
1361 runtime::runtime_handle_by_id(self.runtime_id())
1362 }
1363
1364 fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1365 self.runtime_handle()
1366 .with_state_arena(|arena| arena.with_typed::<T, R>(self.state_id(), f))
1367 }
1368
1369 fn try_with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
1370 self.runtime_handle_opt()?
1371 .try_with_state_arena(|arena| arena.with_typed_opt::<T, R>(self.state_id(), f))?
1372 }
1373
1374 fn subscribe_current_scope(&self) {
1375 self.with_inner(register_current_state_scope::<T>);
1376 }
1377
1378 pub fn is_alive(&self) -> bool {
1379 self.try_with_inner(|_| ()).is_some()
1380 }
1381
1382 pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1383 self.try_with_inner(|inner| inner.state.try_with_value(f))?
1384 }
1385
1386 pub fn try_value(&self) -> Option<T> {
1387 self.try_with_inner(|inner| inner.state.try_get())?
1388 }
1389
1390 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1391 self.subscribe_current_scope();
1392 self.with_inner(|inner| inner.with_value(f))
1393 }
1394
1395 pub fn value(&self) -> T {
1396 self.subscribe_current_scope();
1397 self.with_inner(|inner| inner.state.get())
1398 }
1399
1400 pub fn get(&self) -> T {
1401 self.value()
1402 }
1403}
1404
1405impl<T: Clone + 'static> MutableState<T> {
1406 pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
1407 runtime.alloc_persistent_state(value)
1408 }
1409
1410 fn from_parts(id: StateId, runtime_id: runtime::RuntimeId) -> Self {
1411 Self {
1412 id,
1413 runtime_id,
1414 _marker: PhantomData,
1415 }
1416 }
1417
1418 pub(crate) fn from_lease(lease: &Rc<runtime::StateHandleLease>) -> Self {
1419 Self::from_parts(lease.id(), lease.runtime().id())
1420 }
1421
1422 fn state_id(&self) -> StateId {
1423 self.id
1424 }
1425
1426 fn runtime_id(&self) -> runtime::RuntimeId {
1427 self.runtime_id
1428 }
1429
1430 fn runtime_handle(&self) -> RuntimeHandle {
1431 runtime::runtime_handle_by_id(self.runtime_id())
1432 .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
1433 }
1434
1435 fn runtime_handle_opt(&self) -> Option<RuntimeHandle> {
1436 runtime::runtime_handle_by_id(self.runtime_id())
1437 }
1438
1439 fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1440 self.runtime_handle()
1441 .with_state_arena(|arena| arena.with_typed::<T, R>(self.state_id(), f))
1442 }
1443
1444 fn try_with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
1445 self.runtime_handle_opt()?
1446 .try_with_state_arena(|arena| arena.with_typed_opt::<T, R>(self.state_id(), f))?
1447 }
1448
1449 pub fn is_alive(&self) -> bool {
1450 self.try_with_inner(|_| ()).is_some()
1451 }
1452
1453 pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1454 self.try_with_inner(|inner| inner.state.try_with_value(f))?
1455 }
1456
1457 pub fn try_value(&self) -> Option<T> {
1458 self.try_with_inner(|inner| inner.state.try_get())?
1459 }
1460
1461 pub fn as_state(&self) -> State<T> {
1462 State {
1463 id: self.id,
1464 runtime_id: self.runtime_id,
1465 _marker: PhantomData,
1466 }
1467 }
1468
1469 pub fn try_retain(&self) -> Option<OwnedMutableState<T>> {
1470 let lease = self
1471 .runtime_handle_opt()?
1472 .retain_state_lease(self.state_id())?;
1473 Some(OwnedMutableState {
1474 state: *self,
1475 _lease: lease,
1476 _marker: PhantomData,
1477 })
1478 }
1479
1480 pub fn retain(&self) -> OwnedMutableState<T> {
1481 self.try_retain()
1482 .unwrap_or_else(|| panic!("state {:?} is no longer alive", self.state_id()))
1483 }
1484
1485 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1486 self.subscribe_current_scope();
1487 self.with_inner(|inner| inner.with_value(f))
1488 }
1489
1490 pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
1491 let runtime = self.runtime_handle();
1492 runtime.assert_ui_thread();
1493 runtime.with_state_arena(|arena| {
1494 arena.with_typed::<T, R>(self.state_id(), |inner| {
1495 let mut value = inner.state.get();
1496 let tracker = UpdateScope::new(inner.state.id());
1497 let result = f(&mut value);
1498 let wrote_elsewhere = tracker.finish();
1499 if !wrote_elsewhere && inner.state.set(value) {
1500 inner.invalidate_watchers();
1501 }
1502 result
1503 })
1504 })
1505 }
1506
1507 pub fn replace(&self, value: T) {
1508 let Some(runtime) = self.runtime_handle_opt() else {
1509 log::debug!(
1510 "MutableState::replace skipped: runtime {:?} dropped",
1511 self.runtime_id()
1512 );
1513 return;
1514 };
1515 runtime.assert_ui_thread();
1516 let replaced = runtime
1517 .try_with_state_arena(|arena| {
1518 arena.with_typed_opt::<T, ()>(self.state_id(), |inner| {
1519 if inner.state.set(value) {
1520 inner.invalidate_watchers();
1521 }
1522 })
1523 })
1524 .flatten();
1525 if replaced.is_none() {
1526 log::debug!(
1527 "MutableState::replace skipped: state cell released (slot={}, gen={})",
1528 self.state_id().slot(),
1529 self.state_id().generation(),
1530 );
1531 }
1532 }
1533
1534 pub fn set_value(&self, value: T) {
1535 self.replace(value);
1536 }
1537
1538 pub fn set(&self, value: T) {
1539 self.replace(value);
1540 }
1541
1542 pub fn value(&self) -> T {
1543 self.subscribe_current_scope();
1544 self.with_inner(|inner| inner.state.get())
1545 }
1546
1547 pub fn get(&self) -> T {
1548 self.value()
1549 }
1550
1551 pub fn get_non_reactive(&self) -> T {
1552 self.with_inner(|inner| inner.state.get())
1553 }
1554
1555 #[doc(hidden)]
1556 pub fn runtime_state_id(&self) -> StateId {
1557 self.state_id()
1558 }
1559
1560 #[doc(hidden)]
1561 pub fn subscribe_current_scope_only(&self) {
1562 self.subscribe_current_scope();
1563 }
1564
1565 fn subscribe_current_scope(&self) {
1566 self.with_inner(register_current_state_scope::<T>);
1567 }
1568
1569 #[cfg(test)]
1570 pub(crate) fn watcher_count(&self) -> usize {
1571 self.with_inner(|inner| inner.watchers.borrow().len())
1572 }
1573
1574 #[cfg(test)]
1575 pub(crate) fn watcher_capacity(&self) -> usize {
1576 self.with_inner(|inner| inner.watchers.borrow().capacity())
1577 }
1578
1579 #[cfg(test)]
1580 pub(crate) fn state_id_for_test(&self) -> StateId {
1581 self.state_id()
1582 }
1583
1584 #[cfg(test)]
1585 pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
1586 self.as_state().subscribe_scope_for_test(scope);
1587 }
1588}
1589
1590impl<T: Clone + 'static> OwnedMutableState<T> {
1591 pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
1592 let lease = runtime.alloc_state(value);
1593 Self {
1594 state: MutableState::from_lease(&lease),
1595 _lease: lease,
1596 _marker: PhantomData,
1597 }
1598 }
1599
1600 pub(crate) fn with_runtime_and_policy(
1601 value: T,
1602 runtime: RuntimeHandle,
1603 policy: Arc<dyn MutationPolicy<T>>,
1604 ) -> Self {
1605 let lease = runtime.alloc_state_with_policy(value, policy);
1606 Self {
1607 state: MutableState::from_lease(&lease),
1608 _lease: lease,
1609 _marker: PhantomData,
1610 }
1611 }
1612
1613 pub fn handle(&self) -> MutableState<T> {
1614 self.state
1615 }
1616
1617 pub fn as_state(&self) -> State<T> {
1618 self.state.as_state()
1619 }
1620}
1621
1622impl<T: Clone + 'static> Deref for OwnedMutableState<T> {
1623 type Target = MutableState<T>;
1624
1625 fn deref(&self) -> &Self::Target {
1626 &self.state
1627 }
1628}
1629
1630#[cfg(test)]
1631impl<T: Clone + 'static> State<T> {
1632 pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
1633 self.with_inner(|inner| {
1634 if inner.register_scope(scope) {
1635 if let Some(state_id) = inner.state_id() {
1636 scope.record_state_subscription(state_id);
1637 }
1638 }
1639 });
1640 }
1641}
1642
1643impl<T: fmt::Debug + Clone + 'static> fmt::Debug for MutableState<T> {
1644 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1645 if let Some(value) = self.try_value() {
1646 f.debug_struct("MutableState")
1647 .field("value", &value)
1648 .finish()
1649 } else {
1650 f.write_str("MutableState { value: <unavailable> }")
1651 }
1652 }
1653}
1654
1655#[derive(Clone)]
1656pub struct SnapshotStateList<T: Clone + 'static> {
1657 state: OwnedMutableState<Vec<T>>,
1658}
1659
1660impl<T: Clone + 'static> SnapshotStateList<T> {
1661 pub fn with_runtime<I>(values: I, runtime: RuntimeHandle) -> Self
1662 where
1663 I: IntoIterator<Item = T>,
1664 {
1665 let initial: Vec<T> = values.into_iter().collect();
1666 Self {
1667 state: OwnedMutableState::with_runtime(initial, runtime),
1668 }
1669 }
1670
1671 pub fn as_state(&self) -> State<Vec<T>> {
1672 self.state.as_state()
1673 }
1674
1675 pub fn as_mutable_state(&self) -> MutableState<Vec<T>> {
1676 self.state.handle()
1677 }
1678
1679 pub fn len(&self) -> usize {
1680 self.state.with(|values| values.len())
1681 }
1682
1683 pub fn is_empty(&self) -> bool {
1684 self.len() == 0
1685 }
1686
1687 pub fn to_vec(&self) -> Vec<T> {
1688 self.state.with(|values| values.clone())
1689 }
1690
1691 pub fn iter(&self) -> Vec<T> {
1692 self.to_vec()
1693 }
1694
1695 pub fn get(&self, index: usize) -> T {
1696 self.state.with(|values| values[index].clone())
1697 }
1698
1699 pub fn get_opt(&self, index: usize) -> Option<T> {
1700 self.state.with(|values| values.get(index).cloned())
1701 }
1702
1703 pub fn first(&self) -> Option<T> {
1704 self.get_opt(0)
1705 }
1706
1707 pub fn last(&self) -> Option<T> {
1708 self.state.with(|values| values.last().cloned())
1709 }
1710
1711 pub fn push(&self, value: T) {
1712 self.state.update(|values| values.push(value));
1713 }
1714
1715 pub fn extend<I>(&self, iter: I)
1716 where
1717 I: IntoIterator<Item = T>,
1718 {
1719 self.state.update(|values| values.extend(iter));
1720 }
1721
1722 pub fn insert(&self, index: usize, value: T) {
1723 self.state.update(|values| values.insert(index, value));
1724 }
1725
1726 pub fn set(&self, index: usize, value: T) -> T {
1727 self.state
1728 .update(|values| std::mem::replace(&mut values[index], value))
1729 }
1730
1731 pub fn remove(&self, index: usize) -> T {
1732 self.state.update(|values| values.remove(index))
1733 }
1734
1735 pub fn pop(&self) -> Option<T> {
1736 self.state.update(|values| values.pop())
1737 }
1738
1739 pub fn clear(&self) {
1740 self.state.replace(Vec::new());
1741 }
1742
1743 pub fn retain<F>(&self, mut predicate: F)
1744 where
1745 F: FnMut(&T) -> bool,
1746 {
1747 self.state
1748 .update(|values| values.retain(|value| predicate(value)));
1749 }
1750
1751 pub fn replace_with<I>(&self, iter: I)
1752 where
1753 I: IntoIterator<Item = T>,
1754 {
1755 self.state.replace(iter.into_iter().collect());
1756 }
1757}
1758
1759impl<T: fmt::Debug + Clone + 'static> fmt::Debug for SnapshotStateList<T> {
1760 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1761 let contents = self.to_vec();
1762 f.debug_struct("SnapshotStateList")
1763 .field("values", &contents)
1764 .finish()
1765 }
1766}
1767
1768#[derive(Clone)]
1769pub struct SnapshotStateMap<K, V>
1770where
1771 K: Clone + Eq + Hash + 'static,
1772 V: Clone + 'static,
1773{
1774 state: OwnedMutableState<HashMap<K, V>>,
1775}
1776
1777impl<K, V> SnapshotStateMap<K, V>
1778where
1779 K: Clone + Eq + Hash + 'static,
1780 V: Clone + 'static,
1781{
1782 pub fn with_runtime<I>(pairs: I, runtime: RuntimeHandle) -> Self
1783 where
1784 I: IntoIterator<Item = (K, V)>,
1785 {
1786 let map: HashMap<K, V> = pairs.into_iter().collect();
1787 Self {
1788 state: OwnedMutableState::with_runtime(map, runtime),
1789 }
1790 }
1791
1792 pub fn as_state(&self) -> State<HashMap<K, V>> {
1793 self.state.as_state()
1794 }
1795
1796 pub fn as_mutable_state(&self) -> MutableState<HashMap<K, V>> {
1797 self.state.handle()
1798 }
1799
1800 pub fn len(&self) -> usize {
1801 self.state.with(|map| map.len())
1802 }
1803
1804 pub fn is_empty(&self) -> bool {
1805 self.state.with(|map| map.is_empty())
1806 }
1807
1808 pub fn contains_key(&self, key: &K) -> bool {
1809 self.state.with(|map| map.contains_key(key))
1810 }
1811
1812 pub fn get(&self, key: &K) -> Option<V> {
1813 self.state.with(|map| map.get(key).cloned())
1814 }
1815
1816 pub fn to_hash_map(&self) -> HashMap<K, V> {
1817 self.state.with(|map| map.clone())
1818 }
1819
1820 pub fn insert(&self, key: K, value: V) -> Option<V> {
1821 self.state.update(|map| map.insert(key, value))
1822 }
1823
1824 pub fn extend<I>(&self, iter: I)
1825 where
1826 I: IntoIterator<Item = (K, V)>,
1827 {
1828 self.state.update(|map| map.extend(iter));
1829 }
1830
1831 pub fn remove(&self, key: &K) -> Option<V> {
1832 self.state.update(|map| map.remove(key))
1833 }
1834
1835 pub fn clear(&self) {
1836 self.state.replace(HashMap::default());
1837 }
1838
1839 pub fn retain<F>(&self, mut predicate: F)
1840 where
1841 F: FnMut(&K, &mut V) -> bool,
1842 {
1843 self.state.update(|map| map.retain(|k, v| predicate(k, v)));
1844 }
1845}
1846
1847impl<K, V> fmt::Debug for SnapshotStateMap<K, V>
1848where
1849 K: Clone + Eq + Hash + fmt::Debug + 'static,
1850 V: Clone + fmt::Debug + 'static,
1851{
1852 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1853 let contents = self.to_hash_map();
1854 f.debug_struct("SnapshotStateMap")
1855 .field("entries", &contents)
1856 .finish()
1857 }
1858}
1859
1860pub(crate) struct DerivedState<T: Clone + 'static> {
1861 compute: Rc<dyn Fn() -> T>,
1862 pub(crate) state: OwnedMutableState<T>,
1863}
1864
1865impl<T: Clone + 'static> DerivedState<T> {
1866 pub(crate) fn new(runtime: RuntimeHandle, compute: Rc<dyn Fn() -> T>) -> Self {
1867 let initial = compute();
1868 Self {
1869 compute,
1870 state: OwnedMutableState::with_runtime(initial, runtime),
1871 }
1872 }
1873
1874 pub(crate) fn set_compute(&mut self, compute: Rc<dyn Fn() -> T>) {
1875 self.compute = compute;
1876 }
1877
1878 pub(crate) fn recompute(&self) {
1879 let value = (self.compute)();
1880 self.state.set_value(value);
1881 }
1882}
1883
1884impl<T: fmt::Debug + Clone + 'static> fmt::Debug for State<T> {
1885 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1886 if let Some(value) = self.try_value() {
1887 f.debug_struct("State").field("value", &value).finish()
1888 } else {
1889 f.write_str("State { value: <unavailable> }")
1890 }
1891 }
1892}
1893
1894#[cfg(test)]
1895mod tests {
1896 use super::*;
1897
1898 fn create_record_chain(ids: &[SnapshotId]) -> Rc<StateRecord> {
1900 let mut head: Option<Rc<StateRecord>> = None;
1901
1902 for &id in ids.iter().rev() {
1904 head = Some(StateRecord::new(id, 0i32, head));
1905 }
1906
1907 head.expect("create_record_chain called with empty ids")
1908 }
1909
1910 struct ManualState {
1911 head: Rc<StateRecord>,
1912 }
1913
1914 impl ManualState {
1915 fn new(head: Rc<StateRecord>) -> Self {
1916 Self { head }
1917 }
1918 }
1919
1920 impl StateObject for ManualState {
1921 fn object_id(&self) -> ObjectId {
1922 ObjectId(999)
1923 }
1924
1925 fn first_record(&self) -> Rc<StateRecord> {
1926 Rc::clone(&self.head)
1927 }
1928
1929 fn try_readable_record(&self, _: SnapshotId, _: &SnapshotIdSet) -> Option<Rc<StateRecord>> {
1930 Some(Rc::clone(&self.head))
1931 }
1932
1933 fn readable_record(&self, _: SnapshotId, _: &SnapshotIdSet) -> Rc<StateRecord> {
1934 Rc::clone(&self.head)
1935 }
1936
1937 fn prepend_state_record(&self, _: Rc<StateRecord>) {}
1938
1939 fn promote_record(&self, _: SnapshotId) -> Result<(), &'static str> {
1940 Ok(())
1941 }
1942
1943 fn as_any(&self) -> &dyn Any {
1944 self
1945 }
1946 }
1947
1948 fn poison_mutex<T>(mutex: &Mutex<T>) {
1949 let poison_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1950 let _guard = mutex
1951 .lock()
1952 .unwrap_or_else(|poisoned| poisoned.into_inner());
1953 panic!("poison snapshot state mutex for recovery test");
1954 }));
1955
1956 assert!(poison_result.is_err());
1957 }
1958
1959 #[test]
1960 fn snapshot_mutable_state_recovers_poisoned_weak_self_lock() {
1961 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1962
1963 poison_mutex(&state.weak_self);
1964
1965 assert_eq!(state.get(), 100);
1966 assert!(state.set(101));
1967 assert_eq!(state.get(), 101);
1968 }
1969
1970 #[test]
1971 fn snapshot_mutable_state_recovers_poisoned_apply_observer_lock() {
1972 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1973 let calls = Rc::new(Cell::new(0usize));
1974 let observed_calls = Rc::clone(&calls);
1975
1976 poison_mutex(&state.apply_observers);
1977
1978 state.add_apply_observer(Box::new(move || {
1979 observed_calls.set(observed_calls.get() + 1);
1980 }));
1981 state.notify_applied();
1982
1983 assert_eq!(calls.get(), 1);
1984 }
1985
1986 #[test]
1987 fn snapshot_mutable_state_promote_missing_record_returns_error() {
1988 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1989 let missing_snapshot = usize::MAX - 17;
1990
1991 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1992 StateObject::promote_record(&*state, missing_snapshot)
1993 }));
1994
1995 assert!(
1996 matches!(result, Ok(Err("missing child record"))),
1997 "missing child record should be reported through Result, got {result:?}"
1998 );
1999 }
2000
2001 #[test]
2002 fn snapshot_mutable_state_promote_wrong_record_type_returns_error() {
2003 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2004 let child_snapshot = usize::MAX - 31;
2005 let wrong_record = StateRecord::new(child_snapshot, "wrong type", None);
2006 StateObject::prepend_state_record(&*state, wrong_record);
2007
2008 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
2009 StateObject::promote_record(&*state, child_snapshot)
2010 }));
2011
2012 assert!(
2013 matches!(result, Ok(Err("child record value missing or wrong type"))),
2014 "wrong child record type should be reported through Result, got {result:?}"
2015 );
2016 }
2017
2018 #[test]
2019 fn snapshot_mutable_state_commit_wrong_record_type_returns_error() {
2020 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2021 let merged = StateRecord::new(usize::MAX - 43, "wrong type", None);
2022
2023 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
2024 StateObject::commit_merged_record(&*state, merged)
2025 }));
2026
2027 assert!(
2028 matches!(result, Ok(Err("merged record value missing or wrong type"))),
2029 "wrong merged record type should be reported through Result, got {result:?}"
2030 );
2031 }
2032
2033 #[test]
2034 fn snapshot_mutable_state_merge_wrong_record_type_returns_none() {
2035 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2036 let previous = StateRecord::new(usize::MAX - 51, 1i32, None);
2037 let current = StateRecord::new(usize::MAX - 52, "wrong type", None);
2038 let applied = StateRecord::new(usize::MAX - 53, 2i32, None);
2039
2040 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
2041 StateObject::merge_records(&*state, previous, current, applied)
2042 }));
2043
2044 match result {
2045 Ok(None) => {}
2046 Ok(Some(_)) => panic!("wrong merge record type unexpectedly produced a merged record"),
2047 Err(_) => panic!("wrong merge record type should not panic"),
2048 }
2049 }
2050
2051 #[test]
2052 fn test_used_locked_finds_invalid_snapshot() {
2053 let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, 0i32, None);
2055 let invalid_rec = StateRecord::new(INVALID_SNAPSHOT_ID, 0i32, Some(tail));
2056 let head = StateRecord::new(10, 0i32, Some(invalid_rec.clone()));
2057
2058 let result = used_locked(&head);
2059 assert!(result.is_some());
2060 assert_eq!(result.unwrap().snapshot_id(), INVALID_SNAPSHOT_ID);
2061 }
2062
2063 #[test]
2064 fn test_used_locked_finds_obscured_record() {
2065 crate::snapshot_pinning::reset_pinning_table();
2067
2068 let pin_handle = crate::snapshot_pinning::track_pinning(10, &SnapshotIdSet::EMPTY);
2071
2072 let oldest = StateRecord::new(2, 0i32, None);
2074 let newer = StateRecord::new(5, 0i32, Some(oldest.clone()));
2075 let head = StateRecord::new(100, 0i32, Some(newer));
2076
2077 let result = used_locked(&head);
2078
2079 assert!(result.is_some());
2081 let reused = result.unwrap();
2082 assert_eq!(
2083 reused.snapshot_id(),
2084 2,
2085 "Should return the oldest obscured record"
2086 );
2087
2088 crate::snapshot_pinning::release_pinning(pin_handle);
2090 }
2091
2092 #[test]
2093 fn test_used_locked_no_reusable_record() {
2094 crate::snapshot_pinning::reset_pinning_table();
2096
2097 let high_id = allocate_record_id() + 1000;
2100 let head = create_record_chain(&[high_id, high_id + 1, high_id + 2]);
2101
2102 let result = used_locked(&head);
2103 assert!(
2104 result.is_none(),
2105 "Should find no reusable records when all are recent"
2106 );
2107 }
2108
2109 #[test]
2110 fn test_used_locked_single_old_record() {
2111 crate::snapshot_pinning::reset_pinning_table();
2113
2114 let old = StateRecord::new(2, 0i32, None);
2116 let head = StateRecord::new(100, 0i32, Some(old));
2117
2118 let result = used_locked(&head);
2119 assert!(result.is_none(), "Single old record should not be reused");
2121 }
2122
2123 #[test]
2124 fn test_readable_record_for_preexisting() {
2125 let head = create_record_chain(&[PREEXISTING_SNAPSHOT_ID]);
2126 let invalid = SnapshotIdSet::EMPTY;
2127
2128 let result = readable_record_for(&head, 10, &invalid);
2129 assert!(result.is_some());
2130 assert_eq!(result.unwrap().snapshot_id(), PREEXISTING_SNAPSHOT_ID);
2131 }
2132
2133 #[test]
2134 fn test_readable_record_for_picks_highest_valid() {
2135 let head = create_record_chain(&[10, 5, PREEXISTING_SNAPSHOT_ID]);
2136 let invalid = SnapshotIdSet::EMPTY;
2137
2138 let result = readable_record_for(&head, 10, &invalid);
2140 assert!(result.is_some());
2141 assert_eq!(result.unwrap().snapshot_id(), 10);
2142
2143 let result = readable_record_for(&head, 7, &invalid);
2145 assert!(result.is_some());
2146 assert_eq!(result.unwrap().snapshot_id(), 5);
2147 }
2148
2149 #[test]
2150 fn test_new_overwritable_record_locked_reuses_invalid() {
2151 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2153
2154 let current_head = state.first_record();
2156 let invalid_rec = StateRecord::new(INVALID_SNAPSHOT_ID, 0i32, current_head.next());
2157 current_head.set_next(Some(invalid_rec.clone()));
2158
2159 let result = new_overwritable_record_locked(&*state);
2160
2161 assert!(Rc::ptr_eq(&result, &invalid_rec));
2163 assert_eq!(result.snapshot_id(), SNAPSHOT_ID_MAX);
2164 }
2165
2166 #[test]
2167 fn test_new_overwritable_record_locked_creates_new() {
2168 crate::snapshot_pinning::reset_pinning_table();
2169
2170 let _pin_handle = crate::snapshot_pinning::track_pinning(1, &SnapshotIdSet::EMPTY);
2173
2174 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2176 let old_head = state.first_record();
2177
2178 let result = new_overwritable_record_locked(&*state);
2179
2180 assert_eq!(result.snapshot_id(), SNAPSHOT_ID_MAX);
2182
2183 let new_head = state.first_record();
2185 assert!(
2186 Rc::ptr_eq(&new_head, &result),
2187 "new_head ({:p}) should equal result ({:p})",
2188 Rc::as_ptr(&new_head),
2189 Rc::as_ptr(&result)
2190 );
2191
2192 assert!(result.next().is_some());
2194 assert!(Rc::ptr_eq(&result.next().unwrap(), &old_head));
2195 }
2196
2197 #[test]
2198 fn test_writable_record_reuses_invalid_record() {
2199 crate::snapshot_pinning::reset_pinning_table();
2200
2201 let state = SnapshotMutableState::new_in_arc(7i32, Arc::new(NeverEqual));
2202
2203 let head = state.first_record();
2205 let invalid = StateRecord::new(INVALID_SNAPSHOT_ID, 0i32, head.next());
2206 head.set_next(Some(invalid.clone()));
2207
2208 let snapshot_id = allocate_record_id();
2209 let result = state.writable_record(snapshot_id, &SnapshotIdSet::EMPTY);
2210
2211 assert!(
2212 Rc::ptr_eq(&result, &invalid),
2213 "Expected writable_record to reuse the INVALID record"
2214 );
2215 assert_eq!(result.snapshot_id(), snapshot_id);
2216 result.with_value(|value: &i32| {
2217 assert_eq!(*value, 7, "Reused record should copy the readable value");
2218 });
2219 assert!(!result.is_tombstone());
2220 }
2221
2222 #[test]
2223 fn test_writable_record_creates_new_when_reuse_disallowed() {
2224 crate::snapshot_pinning::reset_pinning_table();
2225 let pin = crate::snapshot_pinning::track_pinning(1, &SnapshotIdSet::EMPTY);
2226
2227 let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2228 let original_head = state.first_record();
2229 let preexisting = original_head
2230 .next()
2231 .expect("preexisting record should exist for newly created state");
2232
2233 let snapshot_id = allocate_record_id();
2234 let result = state.writable_record(snapshot_id, &SnapshotIdSet::EMPTY);
2235
2236 assert!(
2237 !Rc::ptr_eq(&result, &original_head),
2238 "Should not reuse the current head when reuse is disallowed"
2239 );
2240 assert!(
2241 !Rc::ptr_eq(&result, &preexisting),
2242 "Should not reuse the PREEXISTING record"
2243 );
2244 assert_eq!(result.snapshot_id(), snapshot_id);
2245 result.with_value(|value: &i32| assert_eq!(*value, 42));
2246
2247 let new_head = state.first_record();
2248 assert!(
2249 Rc::ptr_eq(&new_head, &result),
2250 "Newly created record should become the head of the chain"
2251 );
2252
2253 crate::snapshot_pinning::release_pinning(pin);
2254 }
2255
2256 #[test]
2257 fn test_state_record_clear_for_reuse() {
2258 let record = StateRecord::new(10, 42i32, None);
2259
2260 record.with_value(|val: &i32| {
2262 assert_eq!(*val, 42);
2263 });
2264
2265 record.clear_for_reuse();
2267
2268 assert_eq!(record.snapshot_id(), 10);
2271 }
2272
2273 #[test]
2274 fn test_overwrite_unused_records_no_old_records() {
2275 crate::snapshot_pinning::reset_pinning_table();
2276
2277 let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2279
2280 let _pin = crate::snapshot_pinning::track_pinning(1, &SnapshotIdSet::EMPTY);
2283
2284 let should_retain = state.overwrite_unused_records();
2285
2286 assert!(
2288 should_retain,
2289 "Should retain multiple records when none are old enough"
2290 );
2291
2292 let mut cursor = Some(state.first_record());
2294 while let Some(record) = cursor {
2295 assert_ne!(record.snapshot_id(), INVALID_SNAPSHOT_ID);
2296 cursor = record.next();
2297 }
2298 }
2299
2300 #[test]
2301 fn test_overwrite_unused_records_basic_cleanup() {
2302 crate::snapshot_pinning::reset_pinning_table();
2304
2305 let rec1 = StateRecord::new(100, 1i32, None);
2307 let rec2 = StateRecord::new(200, 2i32, Some(rec1.clone()));
2308 let rec3 = StateRecord::new(300, 3i32, Some(rec2.clone()));
2309
2310 struct TestState {
2312 head: Rc<StateRecord>,
2313 }
2314 impl StateObject for TestState {
2315 fn object_id(&self) -> ObjectId {
2316 ObjectId(999)
2317 }
2318 fn first_record(&self) -> Rc<StateRecord> {
2319 Rc::clone(&self.head)
2320 }
2321 fn try_readable_record(
2322 &self,
2323 _: SnapshotId,
2324 _: &SnapshotIdSet,
2325 ) -> Option<Rc<StateRecord>> {
2326 Some(Rc::clone(&self.head))
2327 }
2328 fn readable_record(&self, _: SnapshotId, _: &SnapshotIdSet) -> Rc<StateRecord> {
2329 Rc::clone(&self.head)
2330 }
2331 fn prepend_state_record(&self, _: Rc<StateRecord>) {}
2332 fn promote_record(&self, _: SnapshotId) -> Result<(), &'static str> {
2333 Ok(())
2334 }
2335 fn as_any(&self) -> &dyn Any {
2336 self
2337 }
2338 }
2339
2340 let test_state = TestState { head: rec3.clone() };
2341
2342 let _pin = crate::snapshot_pinning::track_pinning(1000, &SnapshotIdSet::EMPTY);
2344
2345 let result = overwrite_unused_records_locked::<i32>(&test_state);
2346
2347 assert_eq!(rec3.snapshot_id(), 300);
2349 assert_eq!(rec2.snapshot_id(), INVALID_SNAPSHOT_ID);
2350 assert_eq!(rec1.snapshot_id(), INVALID_SNAPSHOT_ID);
2351
2352 assert!(!result);
2354 }
2355
2356 #[test]
2357 fn test_overwrite_unused_records_single_record_only() {
2358 crate::snapshot_pinning::reset_pinning_table();
2359
2360 let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2361
2362 let head = state.first_record();
2364 head.set_next(None);
2365
2366 let should_retain = state.overwrite_unused_records();
2367
2368 assert!(!should_retain, "Single record should return false");
2370 }
2371
2372 #[test]
2373 fn snapshot_state_try_get_reports_missing_visible_record_without_panicking() {
2374 crate::snapshot_pinning::reset_pinning_table();
2375
2376 let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2377 let head = state.first_record();
2378 head.set_snapshot_id(SNAPSHOT_ID_MAX);
2379 head.set_next(None);
2380
2381 assert_eq!(state.try_get(), None);
2382 }
2383
2384 #[test]
2385 fn test_overwrite_unused_records_clears_values() {
2386 crate::snapshot_pinning::reset_pinning_table();
2387
2388 let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, 0i32, None);
2389 let old_rec1 = StateRecord::new(2, 999i32, Some(tail.clone()));
2390 let old_rec2 = StateRecord::new(3, 888i32, Some(old_rec1.clone()));
2391 let head = StateRecord::new(150, 42i32, Some(old_rec2.clone()));
2392 let state = ManualState::new(head.clone());
2393
2394 old_rec1.with_value(|val: &i32| {
2396 assert_eq!(*val, 999);
2397 });
2398
2399 let _pin = crate::snapshot_pinning::track_pinning(100, &SnapshotIdSet::EMPTY);
2400 overwrite_unused_records_locked::<i32>(&state);
2401
2402 assert_eq!(old_rec1.snapshot_id(), INVALID_SNAPSHOT_ID);
2404 }
2406
2407 #[test]
2408 fn test_overwrite_unused_records_mixed_old_and_new() {
2409 crate::snapshot_pinning::reset_pinning_table();
2410
2411 let preexisting = StateRecord::new(PREEXISTING_SNAPSHOT_ID, 0i32, None);
2413 let rec2 = StateRecord::new(2, 100i32, Some(preexisting.clone()));
2414 let rec5 = StateRecord::new(5, 100i32, Some(rec2.clone()));
2415 let rec50 = StateRecord::new(50, 100i32, Some(rec5.clone()));
2416 let head = StateRecord::new(120, 100i32, Some(rec50.clone()));
2417 let state = ManualState::new(head.clone());
2418
2419 let _pin = crate::snapshot_pinning::track_pinning(40, &SnapshotIdSet::EMPTY);
2421
2422 let should_retain = overwrite_unused_records_locked::<i32>(&state);
2423 assert!(should_retain);
2424
2425 assert_eq!(rec50.snapshot_id(), 50);
2427 assert_eq!(rec5.snapshot_id(), 5);
2429 assert_eq!(rec2.snapshot_id(), INVALID_SNAPSHOT_ID);
2431 }
2432
2433 #[test]
2434 fn test_readable_record_for_skips_invalid_set() {
2435 let head = create_record_chain(&[10, 5, PREEXISTING_SNAPSHOT_ID]);
2436 let invalid = SnapshotIdSet::new().set(5);
2437
2438 let result = readable_record_for(&head, 10, &invalid);
2440 assert!(result.is_some());
2441 assert_eq!(result.unwrap().snapshot_id(), 10);
2442
2443 let result = readable_record_for(&head, 7, &invalid);
2445 assert!(result.is_some());
2446 assert_eq!(result.unwrap().snapshot_id(), PREEXISTING_SNAPSHOT_ID);
2447 }
2448
2449 #[test]
2452 fn test_assign_value_copies_int() {
2453 let source = StateRecord::new(10, 42i32, None);
2454 let target = StateRecord::new(20, 0i32, None);
2455
2456 target.assign_value::<i32>(&source).expect("copy int value");
2457
2458 target.with_value(|val: &i32| {
2460 assert_eq!(*val, 42);
2461 });
2462
2463 source.with_value(|val: &i32| {
2465 assert_eq!(*val, 42);
2466 });
2467
2468 assert_eq!(source.snapshot_id(), 10);
2470 assert_eq!(target.snapshot_id(), 20);
2471 }
2472
2473 #[test]
2474 fn test_assign_value_copies_string() {
2475 let source = StateRecord::new(10, "hello".to_string(), None);
2476 let target = StateRecord::new(20, "world".to_string(), None);
2477
2478 target
2479 .assign_value::<String>(&source)
2480 .expect("copy string value");
2481
2482 target.with_value(|val: &String| {
2484 assert_eq!(val, "hello");
2485 });
2486
2487 source.with_value(|val: &String| {
2489 assert_eq!(val, "hello");
2490 });
2491 }
2492
2493 #[test]
2494 fn test_assign_value_reports_cleared_source() {
2495 let source = StateRecord::new(10, 42i32, None);
2496 let target = StateRecord::new(20, 0i32, None);
2497
2498 source.clear_value();
2499
2500 assert_eq!(
2501 target.assign_value::<i32>(&source),
2502 Err(StateRecordValueError::MissingOrWrongType {
2503 expected: std::any::type_name::<i32>(),
2504 })
2505 );
2506 assert_eq!(target.with_value(|val: &i32| *val), 0);
2507 }
2508
2509 #[test]
2510 fn test_assign_value_overwrites_existing_value() {
2511 let source = StateRecord::new(10, 100i32, None);
2512 let target = StateRecord::new(20, 999i32, None);
2513
2514 target.with_value(|val: &i32| {
2516 assert_eq!(*val, 999);
2517 });
2518
2519 target
2521 .assign_value::<i32>(&source)
2522 .expect("overwrite int value");
2523
2524 target.with_value(|val: &i32| {
2526 assert_eq!(*val, 100);
2527 });
2528 }
2529
2530 #[test]
2531 fn test_assign_value_with_custom_type() {
2532 #[derive(Clone, PartialEq, Debug)]
2533 struct Point {
2534 x: f64,
2535 y: f64,
2536 }
2537
2538 let source = StateRecord::new(10, Point { x: 1.5, y: 2.5 }, None);
2539 let target = StateRecord::new(20, Point { x: 0.0, y: 0.0 }, None);
2540
2541 target
2542 .assign_value::<Point>(&source)
2543 .expect("copy point value");
2544
2545 target.with_value(|val: &Point| {
2546 assert_eq!(val, &Point { x: 1.5, y: 2.5 });
2547 });
2548 }
2549
2550 #[test]
2551 fn test_assign_value_self_assignment() {
2552 let record = StateRecord::new(10, 42i32, None);
2553
2554 record
2556 .assign_value::<i32>(&record)
2557 .expect("self-assign int value");
2558
2559 record.with_value(|val: &i32| {
2560 assert_eq!(*val, 42);
2561 });
2562 }
2563
2564 #[test]
2571 fn event_loop_writes_keep_the_record_chain_bounded() {
2572 crate::snapshot_pinning::reset_pinning_table();
2573 let state = SnapshotMutableState::new_in_arc(0.0f32, Arc::new(NeverEqual));
2574
2575 let mut lens = Vec::new();
2576 for event in 0..3000usize {
2577 crate::run_in_mutable_snapshot(|| {
2578 state.set(event as f32);
2579 })
2580 .expect("event snapshot applies");
2581 let _ = state.get();
2584 if event % 500 == 499 {
2585 lens.push(state.record_chain_debug().len());
2586 }
2587 }
2588
2589 let final_len = *lens.last().expect("sampled chain lengths");
2590 assert!(
2591 final_len <= 16,
2592 "record chain grew without bound across event-loop writes: {lens:?}"
2593 );
2594 }
2595
2596 #[test]
2597 fn test_assign_value_with_vec() {
2598 let source = StateRecord::new(10, vec![1, 2, 3, 4, 5], None);
2599 let target = StateRecord::new(20, Vec::<i32>::new(), None);
2600
2601 target
2602 .assign_value::<Vec<i32>>(&source)
2603 .expect("copy vec value");
2604
2605 target.with_value(|val: &Vec<i32>| {
2606 assert_eq!(val, &vec![1, 2, 3, 4, 5]);
2607 });
2608
2609 source.replace_value(vec![10, 20]);
2611 target.with_value(|val: &Vec<i32>| {
2612 assert_eq!(val, &vec![1, 2, 3, 4, 5]);
2613 });
2614 }
2615}