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();
1113 let current_head = self.head.clone_head();
1114 let new_head = StateRecord::new(new_id, cloned, Some(current_head));
1115 self.head.replace(new_head);
1116 advance_global_snapshot(new_id);
1117 self.notify_applied();
1118 self.assert_chain_integrity("promote_record", Some(child_id));
1119 return Ok(());
1120 }
1121 cursor = record.next();
1122 }
1123 log::error!(
1124 "SnapshotMutableState::promote_record missing child record (state {:?}, child_id={})",
1125 self.id,
1126 child_id
1127 );
1128 Err("missing child record")
1129 }
1130
1131 fn commit_merged_record(&self, merged: Rc<StateRecord>) -> Result<SnapshotId, &'static str> {
1132 let Some(value) = merged.try_with_value(|value: &T| value.clone()) else {
1133 log::error!(
1134 "SnapshotMutableState::commit_merged_record merged record value missing or wrong type (state {:?}, merged_id={})",
1135 self.id,
1136 merged.snapshot_id()
1137 );
1138 return Err("merged record value missing or wrong type");
1139 };
1140 let new_id = allocate_record_id();
1141 let current_head = self.head.clone_head();
1142 let new_head = StateRecord::new(new_id, value, Some(current_head));
1143 self.head.replace(new_head);
1144 advance_global_snapshot(new_id);
1145 self.notify_applied();
1146 self.assert_chain_integrity("commit_merged_record", Some(new_id));
1147 Ok(new_id)
1148 }
1149
1150 fn overwrite_unused_records(&self) -> bool {
1151 overwrite_unused_records_locked::<T>(self)
1152 }
1153
1154 fn as_any(&self) -> &dyn Any {
1155 self
1156 }
1157}
1158
1159pub(crate) struct MutableStateInner<T: Clone + 'static> {
1160 pub(crate) state: Arc<SnapshotMutableState<T>>,
1161 pub(crate) watchers: RefCell<HashMap<ScopeId, RcWeak<RecomposeScopeInner>>>,
1162 runtime: RuntimeHandle,
1163 state_id: Cell<Option<StateId>>,
1164}
1165
1166fn shrink_watchers_if_sparse(watchers: &mut HashMap<ScopeId, RcWeak<RecomposeScopeInner>>) {
1167 let len = watchers.len();
1168 let capacity = watchers.capacity();
1169 if capacity > len.saturating_mul(4).max(32) {
1170 watchers.shrink_to_fit();
1171 }
1172}
1173
1174impl<T: Clone + 'static> MutableStateInner<T> {
1175 pub(crate) fn new_with_policy(
1176 value: T,
1177 runtime: RuntimeHandle,
1178 policy: Arc<dyn MutationPolicy<T>>,
1179 ) -> Self {
1180 Self {
1181 state: SnapshotMutableState::new_in_arc(value, policy),
1182 watchers: RefCell::new(HashMap::default()),
1183 runtime,
1184 state_id: Cell::new(None),
1185 }
1186 }
1187
1188 pub(crate) fn install_snapshot_observer(&self, state_id: StateId) {
1189 self.state_id.set(Some(state_id));
1190 let runtime_handle = self.runtime.clone();
1191 self.state.add_apply_observer(Box::new(move || {
1192 let runtime = runtime_handle.clone();
1193 runtime_handle.enqueue_ui_task(Box::new(move || {
1194 runtime.with_state_arena(|arena| {
1195 let _ = arena.with_typed_opt::<T, _>(state_id, |inner| {
1196 inner.invalidate_watchers();
1197 });
1198 });
1199 }));
1200 }));
1201 }
1202
1203 fn with_value<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1204 let value = self.state.get();
1205 f(&value)
1206 }
1207
1208 fn register_scope(&self, scope: &RecomposeScope) -> bool {
1209 let mut watchers = self.watchers.borrow_mut();
1210 match watchers.get(&scope.id()) {
1211 Some(existing) if existing.upgrade().is_some() => false,
1212 _ => {
1213 watchers.insert(scope.id(), scope.downgrade());
1214 true
1215 }
1216 }
1217 }
1218
1219 pub(crate) fn unregister_scope(&self, scope_id: ScopeId) {
1220 let mut watchers = self.watchers.borrow_mut();
1221 if watchers
1226 .get(&scope_id)
1227 .is_some_and(|weak| weak.upgrade().is_none())
1228 {
1229 watchers.remove(&scope_id);
1230 shrink_watchers_if_sparse(&mut watchers);
1231 }
1232 }
1233
1234 fn state_id(&self) -> Option<StateId> {
1235 self.state_id.get()
1236 }
1237
1238 fn invalidate_watchers(&self) {
1239 let watchers: Vec<RecomposeScope> = {
1240 let mut watchers = self.watchers.borrow_mut();
1241 let mut live = Vec::with_capacity(watchers.len());
1242 watchers.retain(|_, scope| {
1243 if let Some(inner) = scope.upgrade() {
1244 live.push(RecomposeScope { inner });
1245 true
1246 } else {
1247 false
1248 }
1249 });
1250 shrink_watchers_if_sparse(&mut watchers);
1251 live
1252 };
1253
1254 for watcher in watchers {
1255 debug_record_scope_invalidation::<T>(watcher.id(), self.state_id.get());
1256 if let Some(state_id) = self.state_id.get() {
1257 watcher.invalidate_from_state(state_id);
1258 } else {
1259 watcher.invalidate();
1260 }
1261 }
1262 }
1263}
1264
1265fn register_current_state_scope<T: Clone + 'static>(inner: &MutableStateInner<T>) {
1266 let Some(Some(scope)) =
1267 with_current_composer_opt(|composer| composer.current_state_invalidation_scope())
1268 else {
1269 return;
1270 };
1271 if inner.register_scope(&scope) {
1272 if let Some(state_id) = inner.state_id() {
1273 scope.record_state_subscription(state_id);
1274 }
1275 }
1276}
1277
1278pub struct State<T: Clone + 'static> {
1280 id: StateId,
1281 runtime_id: runtime::RuntimeId,
1282 _marker: PhantomData<fn() -> T>,
1283}
1284
1285pub struct MutableState<T: Clone + 'static> {
1291 id: StateId,
1292 runtime_id: runtime::RuntimeId,
1293 _marker: PhantomData<fn() -> T>,
1294}
1295
1296#[derive(Clone)]
1298pub struct OwnedMutableState<T: Clone + 'static> {
1299 state: MutableState<T>,
1300 _lease: Rc<runtime::StateHandleLease>,
1301 _marker: PhantomData<fn() -> T>,
1302}
1303
1304impl<T: Clone + 'static> PartialEq for State<T> {
1305 fn eq(&self, other: &Self) -> bool {
1306 self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
1307 }
1308}
1309
1310impl<T: Clone + 'static> Eq for State<T> {}
1311
1312impl<T: Clone + 'static> PartialEq for MutableState<T> {
1313 fn eq(&self, other: &Self) -> bool {
1314 self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
1315 }
1316}
1317
1318impl<T: Clone + 'static> Eq for MutableState<T> {}
1319
1320impl<T: Clone + 'static> Copy for State<T> {}
1321
1322impl<T: Clone + 'static> Clone for State<T> {
1323 fn clone(&self) -> Self {
1324 *self
1325 }
1326}
1327
1328impl<T: Clone + 'static> Copy for MutableState<T> {}
1329
1330impl<T: Clone + 'static> Clone for MutableState<T> {
1331 fn clone(&self) -> Self {
1332 *self
1333 }
1334}
1335
1336impl<T: Clone + 'static> State<T> {
1337 fn state_id(&self) -> StateId {
1338 self.id
1339 }
1340
1341 fn runtime_id(&self) -> runtime::RuntimeId {
1342 self.runtime_id
1343 }
1344
1345 fn runtime_handle(&self) -> RuntimeHandle {
1346 runtime::runtime_handle_by_id(self.runtime_id())
1347 .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
1348 }
1349
1350 fn runtime_handle_opt(&self) -> Option<RuntimeHandle> {
1351 runtime::runtime_handle_by_id(self.runtime_id())
1352 }
1353
1354 fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1355 self.runtime_handle()
1356 .with_state_arena(|arena| arena.with_typed::<T, R>(self.state_id(), f))
1357 }
1358
1359 fn try_with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
1360 self.runtime_handle_opt()?
1361 .try_with_state_arena(|arena| arena.with_typed_opt::<T, R>(self.state_id(), f))?
1362 }
1363
1364 fn subscribe_current_scope(&self) {
1365 self.with_inner(register_current_state_scope::<T>);
1366 }
1367
1368 pub fn is_alive(&self) -> bool {
1369 self.try_with_inner(|_| ()).is_some()
1370 }
1371
1372 pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1373 self.try_with_inner(|inner| inner.state.try_with_value(f))?
1374 }
1375
1376 pub fn try_value(&self) -> Option<T> {
1377 self.try_with_inner(|inner| inner.state.try_get())?
1378 }
1379
1380 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1381 self.subscribe_current_scope();
1382 self.with_inner(|inner| inner.with_value(f))
1383 }
1384
1385 pub fn value(&self) -> T {
1386 self.subscribe_current_scope();
1387 self.with_inner(|inner| inner.state.get())
1388 }
1389
1390 pub fn get(&self) -> T {
1391 self.value()
1392 }
1393}
1394
1395impl<T: Clone + 'static> MutableState<T> {
1396 pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
1397 runtime.alloc_persistent_state(value)
1398 }
1399
1400 fn from_parts(id: StateId, runtime_id: runtime::RuntimeId) -> Self {
1401 Self {
1402 id,
1403 runtime_id,
1404 _marker: PhantomData,
1405 }
1406 }
1407
1408 pub(crate) fn from_lease(lease: &Rc<runtime::StateHandleLease>) -> Self {
1409 Self::from_parts(lease.id(), lease.runtime().id())
1410 }
1411
1412 fn state_id(&self) -> StateId {
1413 self.id
1414 }
1415
1416 fn runtime_id(&self) -> runtime::RuntimeId {
1417 self.runtime_id
1418 }
1419
1420 fn runtime_handle(&self) -> RuntimeHandle {
1421 runtime::runtime_handle_by_id(self.runtime_id())
1422 .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
1423 }
1424
1425 fn runtime_handle_opt(&self) -> Option<RuntimeHandle> {
1426 runtime::runtime_handle_by_id(self.runtime_id())
1427 }
1428
1429 fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1430 self.runtime_handle()
1431 .with_state_arena(|arena| arena.with_typed::<T, R>(self.state_id(), f))
1432 }
1433
1434 fn try_with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
1435 self.runtime_handle_opt()?
1436 .try_with_state_arena(|arena| arena.with_typed_opt::<T, R>(self.state_id(), f))?
1437 }
1438
1439 pub fn is_alive(&self) -> bool {
1440 self.try_with_inner(|_| ()).is_some()
1441 }
1442
1443 pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1444 self.try_with_inner(|inner| inner.state.try_with_value(f))?
1445 }
1446
1447 pub fn try_value(&self) -> Option<T> {
1448 self.try_with_inner(|inner| inner.state.try_get())?
1449 }
1450
1451 pub fn as_state(&self) -> State<T> {
1452 State {
1453 id: self.id,
1454 runtime_id: self.runtime_id,
1455 _marker: PhantomData,
1456 }
1457 }
1458
1459 pub fn try_retain(&self) -> Option<OwnedMutableState<T>> {
1460 let lease = self
1461 .runtime_handle_opt()?
1462 .retain_state_lease(self.state_id())?;
1463 Some(OwnedMutableState {
1464 state: *self,
1465 _lease: lease,
1466 _marker: PhantomData,
1467 })
1468 }
1469
1470 pub fn retain(&self) -> OwnedMutableState<T> {
1471 self.try_retain()
1472 .unwrap_or_else(|| panic!("state {:?} is no longer alive", self.state_id()))
1473 }
1474
1475 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1476 self.subscribe_current_scope();
1477 self.with_inner(|inner| inner.with_value(f))
1478 }
1479
1480 pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
1481 let runtime = self.runtime_handle();
1482 runtime.assert_ui_thread();
1483 runtime.with_state_arena(|arena| {
1484 arena.with_typed::<T, R>(self.state_id(), |inner| {
1485 let mut value = inner.state.get();
1486 let tracker = UpdateScope::new(inner.state.id());
1487 let result = f(&mut value);
1488 let wrote_elsewhere = tracker.finish();
1489 if !wrote_elsewhere && inner.state.set(value) {
1490 inner.invalidate_watchers();
1491 }
1492 result
1493 })
1494 })
1495 }
1496
1497 pub fn replace(&self, value: T) {
1498 let Some(runtime) = self.runtime_handle_opt() else {
1499 log::debug!(
1500 "MutableState::replace skipped: runtime {:?} dropped",
1501 self.runtime_id()
1502 );
1503 return;
1504 };
1505 runtime.assert_ui_thread();
1506 let replaced = runtime
1507 .try_with_state_arena(|arena| {
1508 arena.with_typed_opt::<T, ()>(self.state_id(), |inner| {
1509 if inner.state.set(value) {
1510 inner.invalidate_watchers();
1511 }
1512 })
1513 })
1514 .flatten();
1515 if replaced.is_none() {
1516 log::debug!(
1517 "MutableState::replace skipped: state cell released (slot={}, gen={})",
1518 self.state_id().slot(),
1519 self.state_id().generation(),
1520 );
1521 }
1522 }
1523
1524 pub fn set_value(&self, value: T) {
1525 self.replace(value);
1526 }
1527
1528 pub fn set(&self, value: T) {
1529 self.replace(value);
1530 }
1531
1532 pub fn value(&self) -> T {
1533 self.subscribe_current_scope();
1534 self.with_inner(|inner| inner.state.get())
1535 }
1536
1537 pub fn get(&self) -> T {
1538 self.value()
1539 }
1540
1541 pub fn get_non_reactive(&self) -> T {
1542 self.with_inner(|inner| inner.state.get())
1543 }
1544
1545 #[doc(hidden)]
1546 pub fn runtime_state_id(&self) -> StateId {
1547 self.state_id()
1548 }
1549
1550 #[doc(hidden)]
1551 pub fn subscribe_current_scope_only(&self) {
1552 self.subscribe_current_scope();
1553 }
1554
1555 fn subscribe_current_scope(&self) {
1556 self.with_inner(register_current_state_scope::<T>);
1557 }
1558
1559 #[cfg(test)]
1560 pub(crate) fn watcher_count(&self) -> usize {
1561 self.with_inner(|inner| inner.watchers.borrow().len())
1562 }
1563
1564 #[cfg(test)]
1565 pub(crate) fn watcher_capacity(&self) -> usize {
1566 self.with_inner(|inner| inner.watchers.borrow().capacity())
1567 }
1568
1569 #[cfg(test)]
1570 pub(crate) fn state_id_for_test(&self) -> StateId {
1571 self.state_id()
1572 }
1573
1574 #[cfg(test)]
1575 pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
1576 self.as_state().subscribe_scope_for_test(scope);
1577 }
1578}
1579
1580impl<T: Clone + 'static> OwnedMutableState<T> {
1581 pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
1582 let lease = runtime.alloc_state(value);
1583 Self {
1584 state: MutableState::from_lease(&lease),
1585 _lease: lease,
1586 _marker: PhantomData,
1587 }
1588 }
1589
1590 pub(crate) fn with_runtime_and_policy(
1591 value: T,
1592 runtime: RuntimeHandle,
1593 policy: Arc<dyn MutationPolicy<T>>,
1594 ) -> Self {
1595 let lease = runtime.alloc_state_with_policy(value, policy);
1596 Self {
1597 state: MutableState::from_lease(&lease),
1598 _lease: lease,
1599 _marker: PhantomData,
1600 }
1601 }
1602
1603 pub fn handle(&self) -> MutableState<T> {
1604 self.state
1605 }
1606
1607 pub fn as_state(&self) -> State<T> {
1608 self.state.as_state()
1609 }
1610}
1611
1612impl<T: Clone + 'static> Deref for OwnedMutableState<T> {
1613 type Target = MutableState<T>;
1614
1615 fn deref(&self) -> &Self::Target {
1616 &self.state
1617 }
1618}
1619
1620#[cfg(test)]
1621impl<T: Clone + 'static> State<T> {
1622 pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
1623 self.with_inner(|inner| {
1624 if inner.register_scope(scope) {
1625 if let Some(state_id) = inner.state_id() {
1626 scope.record_state_subscription(state_id);
1627 }
1628 }
1629 });
1630 }
1631}
1632
1633impl<T: fmt::Debug + Clone + 'static> fmt::Debug for MutableState<T> {
1634 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1635 if let Some(value) = self.try_value() {
1636 f.debug_struct("MutableState")
1637 .field("value", &value)
1638 .finish()
1639 } else {
1640 f.write_str("MutableState { value: <unavailable> }")
1641 }
1642 }
1643}
1644
1645#[derive(Clone)]
1646pub struct SnapshotStateList<T: Clone + 'static> {
1647 state: OwnedMutableState<Vec<T>>,
1648}
1649
1650impl<T: Clone + 'static> SnapshotStateList<T> {
1651 pub fn with_runtime<I>(values: I, runtime: RuntimeHandle) -> Self
1652 where
1653 I: IntoIterator<Item = T>,
1654 {
1655 let initial: Vec<T> = values.into_iter().collect();
1656 Self {
1657 state: OwnedMutableState::with_runtime(initial, runtime),
1658 }
1659 }
1660
1661 pub fn as_state(&self) -> State<Vec<T>> {
1662 self.state.as_state()
1663 }
1664
1665 pub fn as_mutable_state(&self) -> MutableState<Vec<T>> {
1666 self.state.handle()
1667 }
1668
1669 pub fn len(&self) -> usize {
1670 self.state.with(|values| values.len())
1671 }
1672
1673 pub fn is_empty(&self) -> bool {
1674 self.len() == 0
1675 }
1676
1677 pub fn to_vec(&self) -> Vec<T> {
1678 self.state.with(|values| values.clone())
1679 }
1680
1681 pub fn iter(&self) -> Vec<T> {
1682 self.to_vec()
1683 }
1684
1685 pub fn get(&self, index: usize) -> T {
1686 self.state.with(|values| values[index].clone())
1687 }
1688
1689 pub fn get_opt(&self, index: usize) -> Option<T> {
1690 self.state.with(|values| values.get(index).cloned())
1691 }
1692
1693 pub fn first(&self) -> Option<T> {
1694 self.get_opt(0)
1695 }
1696
1697 pub fn last(&self) -> Option<T> {
1698 self.state.with(|values| values.last().cloned())
1699 }
1700
1701 pub fn push(&self, value: T) {
1702 self.state.update(|values| values.push(value));
1703 }
1704
1705 pub fn extend<I>(&self, iter: I)
1706 where
1707 I: IntoIterator<Item = T>,
1708 {
1709 self.state.update(|values| values.extend(iter));
1710 }
1711
1712 pub fn insert(&self, index: usize, value: T) {
1713 self.state.update(|values| values.insert(index, value));
1714 }
1715
1716 pub fn set(&self, index: usize, value: T) -> T {
1717 self.state
1718 .update(|values| std::mem::replace(&mut values[index], value))
1719 }
1720
1721 pub fn remove(&self, index: usize) -> T {
1722 self.state.update(|values| values.remove(index))
1723 }
1724
1725 pub fn pop(&self) -> Option<T> {
1726 self.state.update(|values| values.pop())
1727 }
1728
1729 pub fn clear(&self) {
1730 self.state.replace(Vec::new());
1731 }
1732
1733 pub fn retain<F>(&self, mut predicate: F)
1734 where
1735 F: FnMut(&T) -> bool,
1736 {
1737 self.state
1738 .update(|values| values.retain(|value| predicate(value)));
1739 }
1740
1741 pub fn replace_with<I>(&self, iter: I)
1742 where
1743 I: IntoIterator<Item = T>,
1744 {
1745 self.state.replace(iter.into_iter().collect());
1746 }
1747}
1748
1749impl<T: fmt::Debug + Clone + 'static> fmt::Debug for SnapshotStateList<T> {
1750 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1751 let contents = self.to_vec();
1752 f.debug_struct("SnapshotStateList")
1753 .field("values", &contents)
1754 .finish()
1755 }
1756}
1757
1758#[derive(Clone)]
1759pub struct SnapshotStateMap<K, V>
1760where
1761 K: Clone + Eq + Hash + 'static,
1762 V: Clone + 'static,
1763{
1764 state: OwnedMutableState<HashMap<K, V>>,
1765}
1766
1767impl<K, V> SnapshotStateMap<K, V>
1768where
1769 K: Clone + Eq + Hash + 'static,
1770 V: Clone + 'static,
1771{
1772 pub fn with_runtime<I>(pairs: I, runtime: RuntimeHandle) -> Self
1773 where
1774 I: IntoIterator<Item = (K, V)>,
1775 {
1776 let map: HashMap<K, V> = pairs.into_iter().collect();
1777 Self {
1778 state: OwnedMutableState::with_runtime(map, runtime),
1779 }
1780 }
1781
1782 pub fn as_state(&self) -> State<HashMap<K, V>> {
1783 self.state.as_state()
1784 }
1785
1786 pub fn as_mutable_state(&self) -> MutableState<HashMap<K, V>> {
1787 self.state.handle()
1788 }
1789
1790 pub fn len(&self) -> usize {
1791 self.state.with(|map| map.len())
1792 }
1793
1794 pub fn is_empty(&self) -> bool {
1795 self.state.with(|map| map.is_empty())
1796 }
1797
1798 pub fn contains_key(&self, key: &K) -> bool {
1799 self.state.with(|map| map.contains_key(key))
1800 }
1801
1802 pub fn get(&self, key: &K) -> Option<V> {
1803 self.state.with(|map| map.get(key).cloned())
1804 }
1805
1806 pub fn to_hash_map(&self) -> HashMap<K, V> {
1807 self.state.with(|map| map.clone())
1808 }
1809
1810 pub fn insert(&self, key: K, value: V) -> Option<V> {
1811 self.state.update(|map| map.insert(key, value))
1812 }
1813
1814 pub fn extend<I>(&self, iter: I)
1815 where
1816 I: IntoIterator<Item = (K, V)>,
1817 {
1818 self.state.update(|map| map.extend(iter));
1819 }
1820
1821 pub fn remove(&self, key: &K) -> Option<V> {
1822 self.state.update(|map| map.remove(key))
1823 }
1824
1825 pub fn clear(&self) {
1826 self.state.replace(HashMap::default());
1827 }
1828
1829 pub fn retain<F>(&self, mut predicate: F)
1830 where
1831 F: FnMut(&K, &mut V) -> bool,
1832 {
1833 self.state.update(|map| map.retain(|k, v| predicate(k, v)));
1834 }
1835}
1836
1837impl<K, V> fmt::Debug for SnapshotStateMap<K, V>
1838where
1839 K: Clone + Eq + Hash + fmt::Debug + 'static,
1840 V: Clone + fmt::Debug + 'static,
1841{
1842 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1843 let contents = self.to_hash_map();
1844 f.debug_struct("SnapshotStateMap")
1845 .field("entries", &contents)
1846 .finish()
1847 }
1848}
1849
1850pub(crate) struct DerivedState<T: Clone + 'static> {
1851 compute: Rc<dyn Fn() -> T>,
1852 pub(crate) state: OwnedMutableState<T>,
1853}
1854
1855impl<T: Clone + 'static> DerivedState<T> {
1856 pub(crate) fn new(runtime: RuntimeHandle, compute: Rc<dyn Fn() -> T>) -> Self {
1857 let initial = compute();
1858 Self {
1859 compute,
1860 state: OwnedMutableState::with_runtime(initial, runtime),
1861 }
1862 }
1863
1864 pub(crate) fn set_compute(&mut self, compute: Rc<dyn Fn() -> T>) {
1865 self.compute = compute;
1866 }
1867
1868 pub(crate) fn recompute(&self) {
1869 let value = (self.compute)();
1870 self.state.set_value(value);
1871 }
1872}
1873
1874impl<T: fmt::Debug + Clone + 'static> fmt::Debug for State<T> {
1875 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1876 if let Some(value) = self.try_value() {
1877 f.debug_struct("State").field("value", &value).finish()
1878 } else {
1879 f.write_str("State { value: <unavailable> }")
1880 }
1881 }
1882}
1883
1884#[cfg(test)]
1885mod tests {
1886 use super::*;
1887
1888 fn create_record_chain(ids: &[SnapshotId]) -> Rc<StateRecord> {
1890 let mut head: Option<Rc<StateRecord>> = None;
1891
1892 for &id in ids.iter().rev() {
1894 head = Some(StateRecord::new(id, 0i32, head));
1895 }
1896
1897 head.expect("create_record_chain called with empty ids")
1898 }
1899
1900 struct ManualState {
1901 head: Rc<StateRecord>,
1902 }
1903
1904 impl ManualState {
1905 fn new(head: Rc<StateRecord>) -> Self {
1906 Self { head }
1907 }
1908 }
1909
1910 impl StateObject for ManualState {
1911 fn object_id(&self) -> ObjectId {
1912 ObjectId(999)
1913 }
1914
1915 fn first_record(&self) -> Rc<StateRecord> {
1916 Rc::clone(&self.head)
1917 }
1918
1919 fn try_readable_record(&self, _: SnapshotId, _: &SnapshotIdSet) -> Option<Rc<StateRecord>> {
1920 Some(Rc::clone(&self.head))
1921 }
1922
1923 fn readable_record(&self, _: SnapshotId, _: &SnapshotIdSet) -> Rc<StateRecord> {
1924 Rc::clone(&self.head)
1925 }
1926
1927 fn prepend_state_record(&self, _: Rc<StateRecord>) {}
1928
1929 fn promote_record(&self, _: SnapshotId) -> Result<(), &'static str> {
1930 Ok(())
1931 }
1932
1933 fn as_any(&self) -> &dyn Any {
1934 self
1935 }
1936 }
1937
1938 fn poison_mutex<T>(mutex: &Mutex<T>) {
1939 let poison_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1940 let _guard = mutex
1941 .lock()
1942 .unwrap_or_else(|poisoned| poisoned.into_inner());
1943 panic!("poison snapshot state mutex for recovery test");
1944 }));
1945
1946 assert!(poison_result.is_err());
1947 }
1948
1949 #[test]
1950 fn snapshot_mutable_state_recovers_poisoned_weak_self_lock() {
1951 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1952
1953 poison_mutex(&state.weak_self);
1954
1955 assert_eq!(state.get(), 100);
1956 assert!(state.set(101));
1957 assert_eq!(state.get(), 101);
1958 }
1959
1960 #[test]
1961 fn snapshot_mutable_state_recovers_poisoned_apply_observer_lock() {
1962 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1963 let calls = Rc::new(Cell::new(0usize));
1964 let observed_calls = Rc::clone(&calls);
1965
1966 poison_mutex(&state.apply_observers);
1967
1968 state.add_apply_observer(Box::new(move || {
1969 observed_calls.set(observed_calls.get() + 1);
1970 }));
1971 state.notify_applied();
1972
1973 assert_eq!(calls.get(), 1);
1974 }
1975
1976 #[test]
1977 fn snapshot_mutable_state_promote_missing_record_returns_error() {
1978 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1979 let missing_snapshot = usize::MAX - 17;
1980
1981 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1982 StateObject::promote_record(&*state, missing_snapshot)
1983 }));
1984
1985 assert!(
1986 matches!(result, Ok(Err("missing child record"))),
1987 "missing child record should be reported through Result, got {result:?}"
1988 );
1989 }
1990
1991 #[test]
1992 fn snapshot_mutable_state_promote_wrong_record_type_returns_error() {
1993 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
1994 let child_snapshot = usize::MAX - 31;
1995 let wrong_record = StateRecord::new(child_snapshot, "wrong type", None);
1996 StateObject::prepend_state_record(&*state, wrong_record);
1997
1998 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1999 StateObject::promote_record(&*state, child_snapshot)
2000 }));
2001
2002 assert!(
2003 matches!(result, Ok(Err("child record value missing or wrong type"))),
2004 "wrong child record type should be reported through Result, got {result:?}"
2005 );
2006 }
2007
2008 #[test]
2009 fn snapshot_mutable_state_commit_wrong_record_type_returns_error() {
2010 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2011 let merged = StateRecord::new(usize::MAX - 43, "wrong type", None);
2012
2013 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
2014 StateObject::commit_merged_record(&*state, merged)
2015 }));
2016
2017 assert!(
2018 matches!(result, Ok(Err("merged record value missing or wrong type"))),
2019 "wrong merged record type should be reported through Result, got {result:?}"
2020 );
2021 }
2022
2023 #[test]
2024 fn snapshot_mutable_state_merge_wrong_record_type_returns_none() {
2025 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2026 let previous = StateRecord::new(usize::MAX - 51, 1i32, None);
2027 let current = StateRecord::new(usize::MAX - 52, "wrong type", None);
2028 let applied = StateRecord::new(usize::MAX - 53, 2i32, None);
2029
2030 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
2031 StateObject::merge_records(&*state, previous, current, applied)
2032 }));
2033
2034 match result {
2035 Ok(None) => {}
2036 Ok(Some(_)) => panic!("wrong merge record type unexpectedly produced a merged record"),
2037 Err(_) => panic!("wrong merge record type should not panic"),
2038 }
2039 }
2040
2041 #[test]
2042 fn test_used_locked_finds_invalid_snapshot() {
2043 let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, 0i32, None);
2045 let invalid_rec = StateRecord::new(INVALID_SNAPSHOT_ID, 0i32, Some(tail));
2046 let head = StateRecord::new(10, 0i32, Some(invalid_rec.clone()));
2047
2048 let result = used_locked(&head);
2049 assert!(result.is_some());
2050 assert_eq!(result.unwrap().snapshot_id(), INVALID_SNAPSHOT_ID);
2051 }
2052
2053 #[test]
2054 fn test_used_locked_finds_obscured_record() {
2055 crate::snapshot_pinning::reset_pinning_table();
2057
2058 let pin_handle = crate::snapshot_pinning::track_pinning(10, &SnapshotIdSet::EMPTY);
2061
2062 let oldest = StateRecord::new(2, 0i32, None);
2064 let newer = StateRecord::new(5, 0i32, Some(oldest.clone()));
2065 let head = StateRecord::new(100, 0i32, Some(newer));
2066
2067 let result = used_locked(&head);
2068
2069 assert!(result.is_some());
2071 let reused = result.unwrap();
2072 assert_eq!(
2073 reused.snapshot_id(),
2074 2,
2075 "Should return the oldest obscured record"
2076 );
2077
2078 crate::snapshot_pinning::release_pinning(pin_handle);
2080 }
2081
2082 #[test]
2083 fn test_used_locked_no_reusable_record() {
2084 crate::snapshot_pinning::reset_pinning_table();
2086
2087 let high_id = allocate_record_id() + 1000;
2090 let head = create_record_chain(&[high_id, high_id + 1, high_id + 2]);
2091
2092 let result = used_locked(&head);
2093 assert!(
2094 result.is_none(),
2095 "Should find no reusable records when all are recent"
2096 );
2097 }
2098
2099 #[test]
2100 fn test_used_locked_single_old_record() {
2101 crate::snapshot_pinning::reset_pinning_table();
2103
2104 let old = StateRecord::new(2, 0i32, None);
2106 let head = StateRecord::new(100, 0i32, Some(old));
2107
2108 let result = used_locked(&head);
2109 assert!(result.is_none(), "Single old record should not be reused");
2111 }
2112
2113 #[test]
2114 fn test_readable_record_for_preexisting() {
2115 let head = create_record_chain(&[PREEXISTING_SNAPSHOT_ID]);
2116 let invalid = SnapshotIdSet::EMPTY;
2117
2118 let result = readable_record_for(&head, 10, &invalid);
2119 assert!(result.is_some());
2120 assert_eq!(result.unwrap().snapshot_id(), PREEXISTING_SNAPSHOT_ID);
2121 }
2122
2123 #[test]
2124 fn test_readable_record_for_picks_highest_valid() {
2125 let head = create_record_chain(&[10, 5, PREEXISTING_SNAPSHOT_ID]);
2126 let invalid = SnapshotIdSet::EMPTY;
2127
2128 let result = readable_record_for(&head, 10, &invalid);
2130 assert!(result.is_some());
2131 assert_eq!(result.unwrap().snapshot_id(), 10);
2132
2133 let result = readable_record_for(&head, 7, &invalid);
2135 assert!(result.is_some());
2136 assert_eq!(result.unwrap().snapshot_id(), 5);
2137 }
2138
2139 #[test]
2140 fn test_new_overwritable_record_locked_reuses_invalid() {
2141 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2143
2144 let current_head = state.first_record();
2146 let invalid_rec = StateRecord::new(INVALID_SNAPSHOT_ID, 0i32, current_head.next());
2147 current_head.set_next(Some(invalid_rec.clone()));
2148
2149 let result = new_overwritable_record_locked(&*state);
2150
2151 assert!(Rc::ptr_eq(&result, &invalid_rec));
2153 assert_eq!(result.snapshot_id(), SNAPSHOT_ID_MAX);
2154 }
2155
2156 #[test]
2157 fn test_new_overwritable_record_locked_creates_new() {
2158 crate::snapshot_pinning::reset_pinning_table();
2159
2160 let _pin_handle = crate::snapshot_pinning::track_pinning(1, &SnapshotIdSet::EMPTY);
2163
2164 let state = SnapshotMutableState::new_in_arc(100i32, Arc::new(NeverEqual));
2166 let old_head = state.first_record();
2167
2168 let result = new_overwritable_record_locked(&*state);
2169
2170 assert_eq!(result.snapshot_id(), SNAPSHOT_ID_MAX);
2172
2173 let new_head = state.first_record();
2175 assert!(
2176 Rc::ptr_eq(&new_head, &result),
2177 "new_head ({:p}) should equal result ({:p})",
2178 Rc::as_ptr(&new_head),
2179 Rc::as_ptr(&result)
2180 );
2181
2182 assert!(result.next().is_some());
2184 assert!(Rc::ptr_eq(&result.next().unwrap(), &old_head));
2185 }
2186
2187 #[test]
2188 fn test_writable_record_reuses_invalid_record() {
2189 crate::snapshot_pinning::reset_pinning_table();
2190
2191 let state = SnapshotMutableState::new_in_arc(7i32, Arc::new(NeverEqual));
2192
2193 let head = state.first_record();
2195 let invalid = StateRecord::new(INVALID_SNAPSHOT_ID, 0i32, head.next());
2196 head.set_next(Some(invalid.clone()));
2197
2198 let snapshot_id = allocate_record_id();
2199 let result = state.writable_record(snapshot_id, &SnapshotIdSet::EMPTY);
2200
2201 assert!(
2202 Rc::ptr_eq(&result, &invalid),
2203 "Expected writable_record to reuse the INVALID record"
2204 );
2205 assert_eq!(result.snapshot_id(), snapshot_id);
2206 result.with_value(|value: &i32| {
2207 assert_eq!(*value, 7, "Reused record should copy the readable value");
2208 });
2209 assert!(!result.is_tombstone());
2210 }
2211
2212 #[test]
2213 fn test_writable_record_creates_new_when_reuse_disallowed() {
2214 crate::snapshot_pinning::reset_pinning_table();
2215 let pin = crate::snapshot_pinning::track_pinning(1, &SnapshotIdSet::EMPTY);
2216
2217 let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2218 let original_head = state.first_record();
2219 let preexisting = original_head
2220 .next()
2221 .expect("preexisting record should exist for newly created state");
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, &original_head),
2228 "Should not reuse the current head when reuse is disallowed"
2229 );
2230 assert!(
2231 !Rc::ptr_eq(&result, &preexisting),
2232 "Should not reuse the PREEXISTING record"
2233 );
2234 assert_eq!(result.snapshot_id(), snapshot_id);
2235 result.with_value(|value: &i32| assert_eq!(*value, 42));
2236
2237 let new_head = state.first_record();
2238 assert!(
2239 Rc::ptr_eq(&new_head, &result),
2240 "Newly created record should become the head of the chain"
2241 );
2242
2243 crate::snapshot_pinning::release_pinning(pin);
2244 }
2245
2246 #[test]
2247 fn test_state_record_clear_for_reuse() {
2248 let record = StateRecord::new(10, 42i32, None);
2249
2250 record.with_value(|val: &i32| {
2252 assert_eq!(*val, 42);
2253 });
2254
2255 record.clear_for_reuse();
2257
2258 assert_eq!(record.snapshot_id(), 10);
2261 }
2262
2263 #[test]
2264 fn test_overwrite_unused_records_no_old_records() {
2265 crate::snapshot_pinning::reset_pinning_table();
2266
2267 let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2269
2270 let _pin = crate::snapshot_pinning::track_pinning(1, &SnapshotIdSet::EMPTY);
2273
2274 let should_retain = state.overwrite_unused_records();
2275
2276 assert!(
2278 should_retain,
2279 "Should retain multiple records when none are old enough"
2280 );
2281
2282 let mut cursor = Some(state.first_record());
2284 while let Some(record) = cursor {
2285 assert_ne!(record.snapshot_id(), INVALID_SNAPSHOT_ID);
2286 cursor = record.next();
2287 }
2288 }
2289
2290 #[test]
2291 fn test_overwrite_unused_records_basic_cleanup() {
2292 crate::snapshot_pinning::reset_pinning_table();
2294
2295 let rec1 = StateRecord::new(100, 1i32, None);
2297 let rec2 = StateRecord::new(200, 2i32, Some(rec1.clone()));
2298 let rec3 = StateRecord::new(300, 3i32, Some(rec2.clone()));
2299
2300 struct TestState {
2302 head: Rc<StateRecord>,
2303 }
2304 impl StateObject for TestState {
2305 fn object_id(&self) -> ObjectId {
2306 ObjectId(999)
2307 }
2308 fn first_record(&self) -> Rc<StateRecord> {
2309 Rc::clone(&self.head)
2310 }
2311 fn try_readable_record(
2312 &self,
2313 _: SnapshotId,
2314 _: &SnapshotIdSet,
2315 ) -> Option<Rc<StateRecord>> {
2316 Some(Rc::clone(&self.head))
2317 }
2318 fn readable_record(&self, _: SnapshotId, _: &SnapshotIdSet) -> Rc<StateRecord> {
2319 Rc::clone(&self.head)
2320 }
2321 fn prepend_state_record(&self, _: Rc<StateRecord>) {}
2322 fn promote_record(&self, _: SnapshotId) -> Result<(), &'static str> {
2323 Ok(())
2324 }
2325 fn as_any(&self) -> &dyn Any {
2326 self
2327 }
2328 }
2329
2330 let test_state = TestState { head: rec3.clone() };
2331
2332 let _pin = crate::snapshot_pinning::track_pinning(1000, &SnapshotIdSet::EMPTY);
2334
2335 let result = overwrite_unused_records_locked::<i32>(&test_state);
2336
2337 assert_eq!(rec3.snapshot_id(), 300);
2339 assert_eq!(rec2.snapshot_id(), INVALID_SNAPSHOT_ID);
2340 assert_eq!(rec1.snapshot_id(), INVALID_SNAPSHOT_ID);
2341
2342 assert!(!result);
2344 }
2345
2346 #[test]
2347 fn test_overwrite_unused_records_single_record_only() {
2348 crate::snapshot_pinning::reset_pinning_table();
2349
2350 let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2351
2352 let head = state.first_record();
2354 head.set_next(None);
2355
2356 let should_retain = state.overwrite_unused_records();
2357
2358 assert!(!should_retain, "Single record should return false");
2360 }
2361
2362 #[test]
2363 fn snapshot_state_try_get_reports_missing_visible_record_without_panicking() {
2364 crate::snapshot_pinning::reset_pinning_table();
2365
2366 let state = SnapshotMutableState::new_in_arc(42i32, Arc::new(NeverEqual));
2367 let head = state.first_record();
2368 head.set_snapshot_id(SNAPSHOT_ID_MAX);
2369 head.set_next(None);
2370
2371 assert_eq!(state.try_get(), None);
2372 }
2373
2374 #[test]
2375 fn test_overwrite_unused_records_clears_values() {
2376 crate::snapshot_pinning::reset_pinning_table();
2377
2378 let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, 0i32, None);
2379 let old_rec1 = StateRecord::new(2, 999i32, Some(tail.clone()));
2380 let old_rec2 = StateRecord::new(3, 888i32, Some(old_rec1.clone()));
2381 let head = StateRecord::new(150, 42i32, Some(old_rec2.clone()));
2382 let state = ManualState::new(head.clone());
2383
2384 old_rec1.with_value(|val: &i32| {
2386 assert_eq!(*val, 999);
2387 });
2388
2389 let _pin = crate::snapshot_pinning::track_pinning(100, &SnapshotIdSet::EMPTY);
2390 overwrite_unused_records_locked::<i32>(&state);
2391
2392 assert_eq!(old_rec1.snapshot_id(), INVALID_SNAPSHOT_ID);
2394 }
2396
2397 #[test]
2398 fn test_overwrite_unused_records_mixed_old_and_new() {
2399 crate::snapshot_pinning::reset_pinning_table();
2400
2401 let preexisting = StateRecord::new(PREEXISTING_SNAPSHOT_ID, 0i32, None);
2403 let rec2 = StateRecord::new(2, 100i32, Some(preexisting.clone()));
2404 let rec5 = StateRecord::new(5, 100i32, Some(rec2.clone()));
2405 let rec50 = StateRecord::new(50, 100i32, Some(rec5.clone()));
2406 let head = StateRecord::new(120, 100i32, Some(rec50.clone()));
2407 let state = ManualState::new(head.clone());
2408
2409 let _pin = crate::snapshot_pinning::track_pinning(40, &SnapshotIdSet::EMPTY);
2411
2412 let should_retain = overwrite_unused_records_locked::<i32>(&state);
2413 assert!(should_retain);
2414
2415 assert_eq!(rec50.snapshot_id(), 50);
2417 assert_eq!(rec5.snapshot_id(), 5);
2419 assert_eq!(rec2.snapshot_id(), INVALID_SNAPSHOT_ID);
2421 }
2422
2423 #[test]
2424 fn test_readable_record_for_skips_invalid_set() {
2425 let head = create_record_chain(&[10, 5, PREEXISTING_SNAPSHOT_ID]);
2426 let invalid = SnapshotIdSet::new().set(5);
2427
2428 let result = readable_record_for(&head, 10, &invalid);
2430 assert!(result.is_some());
2431 assert_eq!(result.unwrap().snapshot_id(), 10);
2432
2433 let result = readable_record_for(&head, 7, &invalid);
2435 assert!(result.is_some());
2436 assert_eq!(result.unwrap().snapshot_id(), PREEXISTING_SNAPSHOT_ID);
2437 }
2438
2439 #[test]
2442 fn test_assign_value_copies_int() {
2443 let source = StateRecord::new(10, 42i32, None);
2444 let target = StateRecord::new(20, 0i32, None);
2445
2446 target.assign_value::<i32>(&source).expect("copy int value");
2447
2448 target.with_value(|val: &i32| {
2450 assert_eq!(*val, 42);
2451 });
2452
2453 source.with_value(|val: &i32| {
2455 assert_eq!(*val, 42);
2456 });
2457
2458 assert_eq!(source.snapshot_id(), 10);
2460 assert_eq!(target.snapshot_id(), 20);
2461 }
2462
2463 #[test]
2464 fn test_assign_value_copies_string() {
2465 let source = StateRecord::new(10, "hello".to_string(), None);
2466 let target = StateRecord::new(20, "world".to_string(), None);
2467
2468 target
2469 .assign_value::<String>(&source)
2470 .expect("copy string value");
2471
2472 target.with_value(|val: &String| {
2474 assert_eq!(val, "hello");
2475 });
2476
2477 source.with_value(|val: &String| {
2479 assert_eq!(val, "hello");
2480 });
2481 }
2482
2483 #[test]
2484 fn test_assign_value_reports_cleared_source() {
2485 let source = StateRecord::new(10, 42i32, None);
2486 let target = StateRecord::new(20, 0i32, None);
2487
2488 source.clear_value();
2489
2490 assert_eq!(
2491 target.assign_value::<i32>(&source),
2492 Err(StateRecordValueError::MissingOrWrongType {
2493 expected: std::any::type_name::<i32>(),
2494 })
2495 );
2496 assert_eq!(target.with_value(|val: &i32| *val), 0);
2497 }
2498
2499 #[test]
2500 fn test_assign_value_overwrites_existing_value() {
2501 let source = StateRecord::new(10, 100i32, None);
2502 let target = StateRecord::new(20, 999i32, None);
2503
2504 target.with_value(|val: &i32| {
2506 assert_eq!(*val, 999);
2507 });
2508
2509 target
2511 .assign_value::<i32>(&source)
2512 .expect("overwrite int value");
2513
2514 target.with_value(|val: &i32| {
2516 assert_eq!(*val, 100);
2517 });
2518 }
2519
2520 #[test]
2521 fn test_assign_value_with_custom_type() {
2522 #[derive(Clone, PartialEq, Debug)]
2523 struct Point {
2524 x: f64,
2525 y: f64,
2526 }
2527
2528 let source = StateRecord::new(10, Point { x: 1.5, y: 2.5 }, None);
2529 let target = StateRecord::new(20, Point { x: 0.0, y: 0.0 }, None);
2530
2531 target
2532 .assign_value::<Point>(&source)
2533 .expect("copy point value");
2534
2535 target.with_value(|val: &Point| {
2536 assert_eq!(val, &Point { x: 1.5, y: 2.5 });
2537 });
2538 }
2539
2540 #[test]
2541 fn test_assign_value_self_assignment() {
2542 let record = StateRecord::new(10, 42i32, None);
2543
2544 record
2546 .assign_value::<i32>(&record)
2547 .expect("self-assign int value");
2548
2549 record.with_value(|val: &i32| {
2550 assert_eq!(*val, 42);
2551 });
2552 }
2553
2554 #[test]
2555 fn test_assign_value_with_vec() {
2556 let source = StateRecord::new(10, vec![1, 2, 3, 4, 5], None);
2557 let target = StateRecord::new(20, Vec::<i32>::new(), None);
2558
2559 target
2560 .assign_value::<Vec<i32>>(&source)
2561 .expect("copy vec value");
2562
2563 target.with_value(|val: &Vec<i32>| {
2564 assert_eq!(val, &vec![1, 2, 3, 4, 5]);
2565 });
2566
2567 source.replace_value(vec![10, 20]);
2569 target.with_value(|val: &Vec<i32>| {
2570 assert_eq!(val, &vec![1, 2, 3, 4, 5]);
2571 });
2572 }
2573}