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