1use std::{
2 any::{Any, TypeId},
3 cell::{Cell, RefCell},
4 fmt,
5 hash::Hash,
6 marker::PhantomData,
7 ops::Deref,
8 rc::{Rc, Weak as RcWeak},
9 sync::{Arc, Mutex, MutexGuard, PoisonError, Weak},
10};
11
12use crate::{
13 RecomposeScope, RecomposeScopeInner, RuntimeHandle, ScopeId, StateId,
14 collections::map::{HashMap, HashSet},
15 debug_trace::debug_record_scope_invalidation,
16 runtime,
17 snapshot_id_set::{SnapshotId, SnapshotIdSet},
18 snapshot_pinning::lowest_pinned_snapshot,
19 snapshot_v2::{
20 AnySnapshot, GlobalSnapshot, advance_global_snapshot, allocate_record_id, current_snapshot,
21 },
22 with_current_composer_opt,
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;
30
31#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, Default)]
32pub struct ObjectId(pub(crate) usize);
33
34impl ObjectId {
35 pub(crate) fn new<T: ?Sized + 'static>(object: &Arc<T>) -> Self {
36 Self(Arc::as_ptr(object) as *const () as usize)
37 }
38
39 #[inline]
40 pub(crate) fn as_usize(self) -> usize {
41 self.0
42 }
43}
44
45pub struct StateRecord {
46 snapshot_id: Cell<SnapshotId>,
47 tombstone: Cell<bool>,
48 next: Cell<Option<Rc<StateRecord>>>,
49 value: RefCell<Option<Box<dyn RecordValue>>>,
50}
51
52pub(crate) trait RecordValue: Any {
53 fn clone_boxed(&self) -> Box<dyn RecordValue>;
54 fn assign_into(&self, target: &mut Option<Box<dyn RecordValue>>);
55}
56
57impl<T: Any + Clone> RecordValue for T {
58 fn clone_boxed(&self) -> Box<dyn RecordValue> {
59 Box::new(self.clone())
60 }
61
62 fn assign_into(&self, target: &mut Option<Box<dyn RecordValue>>) {
63 match target
64 .as_mut()
65 .and_then(|value| (&mut **value as &mut dyn Any).downcast_mut::<T>())
66 {
67 Some(slot) => slot.clone_from(self),
68 None => *target = Some(Box::new(self.clone())),
69 }
70 }
71}
72
73#[derive(Debug)]
74struct StateReadFailure {
75 state_id: ObjectId,
76 snapshot_id: SnapshotId,
77 fresh_snapshot_id: SnapshotId,
78 fresh_invalid: SnapshotIdSet,
79 record_chain: Vec<(SnapshotId, bool)>,
80}
81
82impl std::fmt::Display for StateReadFailure {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 write!(
85 f,
86 "Reading a state that was created after the snapshot was taken or in a snapshot that has not yet been applied\n\
87 state={:?}, snapshot_id={}, fresh_snapshot_id={}, fresh_invalid={:?}\n\
88 record_chain={:?}",
89 self.state_id,
90 self.snapshot_id,
91 self.fresh_snapshot_id,
92 self.fresh_invalid,
93 self.record_chain
94 )
95 }
96}
97
98#[derive(Debug, Clone, Copy, Eq, PartialEq)]
99pub(crate) enum StateRecordValueError {
100 Missing,
101}
102
103impl StateRecord {
104 pub(crate) fn new<T: Any + Clone>(
105 snapshot_id: SnapshotId,
106 value: T,
107 next: Option<Rc<StateRecord>>,
108 ) -> Rc<Self> {
109 Self::with_boxed(snapshot_id, Box::new(value), next)
110 }
111
112 fn with_boxed(
113 snapshot_id: SnapshotId,
114 value: Box<dyn RecordValue>,
115 next: Option<Rc<StateRecord>>,
116 ) -> Rc<Self> {
117 Rc::new(Self {
118 snapshot_id: Cell::new(snapshot_id),
119 tombstone: Cell::new(false),
120 next: Cell::new(next),
121 value: RefCell::new(Some(value)),
122 })
123 }
124
125 #[inline]
126 pub(crate) fn snapshot_id(&self) -> SnapshotId {
127 self.snapshot_id.get()
128 }
129
130 #[inline]
131 pub(crate) fn set_snapshot_id(&self, id: SnapshotId) {
132 self.snapshot_id.set(id);
133 }
134
135 #[inline]
136 pub(crate) fn next(&self) -> Option<Rc<StateRecord>> {
137 self.next.take().inspect(|record| {
138 self.next.set(Some(Rc::clone(record)));
139 })
140 }
141
142 #[inline]
143 pub(crate) fn set_next(&self, next: Option<Rc<StateRecord>>) {
144 self.next.set(next);
145 }
146
147 #[inline]
148 pub(crate) fn is_tombstone(&self) -> bool {
149 self.tombstone.get()
150 }
151
152 #[inline]
153 pub(crate) fn set_tombstone(&self, tombstone: bool) {
154 self.tombstone.set(tombstone);
155 }
156
157 pub(crate) fn clear_value(&self) {
158 self.value.borrow_mut().take();
159 }
160
161 pub(crate) fn replace_value<T: Any + Clone>(&self, new_value: T) {
162 let mut value = self.value.borrow_mut();
163 match value
164 .as_mut()
165 .and_then(|current| (&mut **current as &mut dyn Any).downcast_mut::<T>())
166 {
167 Some(slot) => *slot = new_value,
168 None => *value = Some(Box::new(new_value)),
169 }
170 }
171
172 fn cloned_value(&self) -> Option<Box<dyn RecordValue>> {
173 self.value
174 .borrow()
175 .as_ref()
176 .map(|value| (**value).clone_boxed())
177 }
178
179 fn cloned_value_of(&self, expected: TypeId) -> Option<Box<dyn RecordValue>> {
180 self.value
181 .borrow()
182 .as_ref()
183 .filter(|value| (&***value as &dyn Any).type_id() == expected)
184 .map(|value| (**value).clone_boxed())
185 }
186
187 fn set_boxed_value(&self, value: Box<dyn RecordValue>) {
188 *self.value.borrow_mut() = Some(value);
189 }
190
191 pub(crate) fn with_value<T: Any, R>(&self, f: impl FnOnce(&T) -> R) -> R {
192 self.try_with_value(f)
193 .unwrap_or_else(|| panic!("StateRecord value missing or wrong type"))
194 }
195
196 pub(crate) fn try_with_value<T: Any, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
197 let guard = self.value.borrow();
198 let value = guard
199 .as_ref()
200 .and_then(|boxed| (&**boxed as &dyn Any).downcast_ref::<T>())?;
201 Some(f(value))
202 }
203
204 #[cfg(test)]
205 pub(crate) fn clear_for_reuse(&self) {
206 self.clear_value();
207 }
208
209 pub(crate) fn assign_value(&self, source: &StateRecord) -> Result<(), StateRecordValueError> {
210 if std::ptr::eq(self, source) {
211 return match self.value.borrow().is_some() {
212 true => Ok(()),
213 false => Err(StateRecordValueError::Missing),
214 };
215 }
216 let source_value = source.value.borrow();
217 let value = source_value
218 .as_ref()
219 .ok_or(StateRecordValueError::Missing)?;
220 (**value).assign_into(&mut self.value.borrow_mut());
221 Ok(())
222 }
223}
224
225impl Drop for StateRecord {
226 fn drop(&mut self) {
227 let mut next = self.next.take();
228 while let Some(node) = next {
229 match Rc::try_unwrap(node) {
230 Ok(record) => {
231 next = record.next.take();
232 }
233 Err(_) => {
234 break;
235 }
236 }
237 }
238 }
239}
240
241struct CurrentRecord {
242 head: RefCell<Rc<StateRecord>>,
243}
244
245impl CurrentRecord {
246 fn new(head: Rc<StateRecord>) -> Self {
247 Self {
248 head: RefCell::new(head),
249 }
250 }
251
252 fn clone_head(&self) -> Rc<StateRecord> {
253 self.head.borrow().clone()
254 }
255
256 fn replace(&self, new_head: Rc<StateRecord>) {
257 *self.head.borrow_mut() = new_head;
258 }
259
260 fn prepend(&self, record: Rc<StateRecord>) {
261 let current_head = self.clone_head();
262 record.set_next(Some(current_head));
263 self.replace(record);
264 }
265}
266
267#[inline]
268fn record_is_valid_for(
269 record: &Rc<StateRecord>,
270 snapshot_id: SnapshotId,
271 invalid: &SnapshotIdSet,
272) -> bool {
273 if record.is_tombstone() {
274 return false;
275 }
276
277 let candidate = record.snapshot_id();
278 if candidate == INVALID_SNAPSHOT_ID || candidate > snapshot_id {
279 return false;
280 }
281
282 candidate == snapshot_id || !invalid.get(candidate)
283}
284
285pub(crate) fn readable_record_for(
286 head: &Rc<StateRecord>,
287 snapshot_id: SnapshotId,
288 invalid: &SnapshotIdSet,
289) -> Option<Rc<StateRecord>> {
290 let mut best: Option<Rc<StateRecord>> = None;
291 let mut cursor = Some(Rc::clone(head));
292
293 while let Some(record) = cursor {
294 cursor = record.next();
295 if record_is_valid_for(&record, snapshot_id, invalid)
296 && best
297 .as_ref()
298 .is_none_or(|current| current.snapshot_id() < record.snapshot_id())
299 {
300 best = Some(record);
301 }
302 }
303
304 best
305}
306
307fn find_youngest_or<F>(head: &Rc<StateRecord>, predicate: F) -> Rc<StateRecord>
308where
309 F: Fn(&Rc<StateRecord>) -> bool,
310{
311 let mut current = Some(Rc::clone(head));
312 let mut youngest = Rc::clone(head);
313
314 while let Some(record) = current {
315 if predicate(&record) {
316 return record;
317 }
318 if youngest.snapshot_id() < record.snapshot_id() {
319 youngest = Rc::clone(&record);
320 }
321 current = record.next();
322 }
323
324 youngest
325}
326
327pub(crate) fn used_locked(head: &Rc<StateRecord>) -> Option<Rc<StateRecord>> {
328 let mut current = Some(Rc::clone(head));
329 let mut valid_record: Option<Rc<StateRecord>> = None;
330
331 let reuse_limit = lowest_pinned_snapshot().map_or_else(
332 || allocate_record_id().saturating_sub(1),
333 |lowest| lowest.saturating_sub(1),
334 );
335
336 let invalid = SnapshotIdSet::EMPTY;
337
338 while let Some(record) = current {
339 let current_id = record.snapshot_id();
340
341 if current_id == PREEXISTING_SNAPSHOT_ID {
342 current = record.next();
343 continue;
344 }
345
346 if current_id == INVALID_SNAPSHOT_ID {
347 return Some(record);
348 }
349
350 if record.is_tombstone() && current_id < reuse_limit {
351 return Some(record);
352 }
353
354 if record_is_valid_for(&record, reuse_limit, &invalid) {
355 if let Some(ref existing) = valid_record {
356 return Some(if current_id < existing.snapshot_id() {
357 record
358 } else {
359 Rc::clone(existing)
360 });
361 } else {
362 valid_record = Some(record.clone());
363 }
364 }
365
366 current = record.next();
367 }
368
369 None
370}
371
372pub(crate) fn new_overwritable_record_locked(state: &dyn StateObject) -> Rc<StateRecord> {
373 let state_head = state.first_record();
374
375 if let Some(reusable) = used_locked(&state_head) {
376 reusable.set_snapshot_id(SNAPSHOT_ID_MAX);
377 return reusable;
378 }
379
380 let new_record = StateRecord::new(SNAPSHOT_ID_MAX, (), None);
381
382 state.prepend_state_record(Rc::clone(&new_record));
383
384 new_record
385}
386
387pub(crate) fn new_overwritable_record_as_head_locked(state: &dyn StateObject) -> Rc<StateRecord> {
388 let head = state.first_record();
389
390 if let Some(reusable) = used_locked(&head) {
391 reusable.set_snapshot_id(SNAPSHOT_ID_MAX);
392
393 if !Rc::ptr_eq(&head, &reusable) {
394 let mut cursor = Some(Rc::clone(&head));
395 let mut unlinked = false;
396
397 while let Some(node) = cursor {
398 let next = node.next();
399 if let Some(next_record) = next {
400 if Rc::ptr_eq(&next_record, &reusable) {
401 node.set_next(reusable.next());
402 unlinked = true;
403 break;
404 }
405 cursor = Some(next_record);
406 } else {
407 break;
408 }
409 }
410
411 if !unlinked {
412 debug_assert!(
413 false,
414 "new_overwritable_record_as_head_locked: reusable record not found in chain"
415 );
416 let new_record = StateRecord::new(SNAPSHOT_ID_MAX, (), None);
417 state.prepend_state_record(Rc::clone(&new_record));
418 return new_record;
419 }
420
421 state.prepend_state_record(Rc::clone(&reusable));
422 }
423
424 return reusable;
425 }
426
427 let new_record = StateRecord::new(SNAPSHOT_ID_MAX, (), None);
428 state.prepend_state_record(Rc::clone(&new_record));
429 new_record
430}
431
432#[cold]
433fn log_merge_value_missing(which: &str, state: ObjectId, record: &StateRecord) {
434 log::error!(
435 "SnapshotMutableState::merge_records {which} record value missing or wrong type (state {state:?}, {which}_id={})",
436 record.snapshot_id()
437 );
438}
439
440fn install_copy_as_head(
441 state: &dyn StateObject,
442 source: &StateRecord,
443 expected: TypeId,
444) -> Option<SnapshotId> {
445 let value = source.cloned_value_of(expected)?;
446 let new_id = allocate_record_id();
447 let record = new_overwritable_record_as_head_locked(state);
448 record.set_boxed_value(value);
449 record.set_tombstone(false);
450 record.set_snapshot_id(new_id);
451 advance_global_snapshot(new_id);
452 Some(new_id)
453}
454
455fn promote_record_locked(
456 state: &dyn StateObject,
457 child_id: SnapshotId,
458 expected: TypeId,
459) -> Result<(), &'static str> {
460 let mut cursor = Some(state.first_record());
461 while let Some(record) = cursor {
462 if record.snapshot_id() == child_id {
463 if install_copy_as_head(state, &record, expected).is_none() {
464 log::error!(
465 "SnapshotMutableState::promote_record child record value missing or wrong type (state {:?}, child_id={})",
466 state.object_id(),
467 child_id
468 );
469 return Err("child record value missing or wrong type");
470 }
471 return Ok(());
472 }
473 cursor = record.next();
474 }
475 log::error!(
476 "SnapshotMutableState::promote_record missing child record (state {:?}, child_id={})",
477 state.object_id(),
478 child_id
479 );
480 Err("missing child record")
481}
482
483fn commit_merged_record_locked(
484 state: &dyn StateObject,
485 merged: &StateRecord,
486 expected: TypeId,
487) -> Result<SnapshotId, &'static str> {
488 install_copy_as_head(state, merged, expected).ok_or_else(|| {
489 log::error!(
490 "SnapshotMutableState::commit_merged_record merged record value missing or wrong type (state {:?}, merged_id={})",
491 state.object_id(),
492 merged.snapshot_id()
493 );
494 "merged record value missing or wrong type"
495 })
496}
497
498pub(crate) fn overwrite_unused_records_locked(state: &dyn StateObject) -> bool {
499 let head = state.first_record();
500 let mut current = Some(Rc::clone(&head));
501 let mut overwrite_record: Option<Rc<StateRecord>> = None;
502 let mut valid_record: Option<Rc<StateRecord>> = None;
503
504 let reuse_limit =
505 lowest_pinned_snapshot().unwrap_or_else(crate::snapshot_v2::peek_next_snapshot_id);
506
507 let mut retained_records = 0;
508
509 while let Some(record) = current {
510 let current_id = record.snapshot_id();
511
512 if current_id == INVALID_SNAPSHOT_ID {
513 } else if current_id < reuse_limit {
514 if valid_record.is_none() {
515 valid_record = Some(Rc::clone(&record));
516 retained_records += 1;
517 } else {
518 let Some(valid) = valid_record.as_ref() else {
519 valid_record = Some(Rc::clone(&record));
520 retained_records += 1;
521 current = record.next();
522 continue;
523 };
524 let record_to_overwrite = if current_id < valid.snapshot_id() {
525 Rc::clone(&record)
526 } else {
527 let to_overwrite = Rc::clone(valid);
528 valid_record = Some(Rc::clone(&record));
529 to_overwrite
530 };
531
532 let source_record = overwrite_record.get_or_insert_with(|| {
533 find_youngest_or(&head, |r| r.snapshot_id() >= reuse_limit)
534 });
535
536 record_to_overwrite.set_snapshot_id(INVALID_SNAPSHOT_ID);
537 if let Err(error) = record_to_overwrite.assign_value(source_record) {
538 log::error!(
539 "snapshot cleanup could not copy retained state record value for state {:?}: {:?}",
540 state.object_id(),
541 error
542 );
543 }
544 }
545 } else {
546 retained_records += 1;
547 }
548
549 current = record.next();
550 }
551
552 retained_records > 1
553}
554
555fn active_snapshot() -> AnySnapshot {
556 current_snapshot().unwrap_or_else(|| AnySnapshot::Global(GlobalSnapshot::get_or_create()))
557}
558
559pub(crate) trait MutationPolicy<T>: Send + Sync {
560 fn equivalent(&self, a: &T, b: &T) -> bool;
561 fn merge(&self, _previous: &T, _current: &T, _applied: &T) -> Option<T> {
562 None
563 }
564}
565
566pub(crate) struct NeverEqual;
567
568impl<T> MutationPolicy<T> for NeverEqual {
569 fn equivalent(&self, _a: &T, _b: &T) -> bool {
570 false
571 }
572}
573
574pub(crate) struct StructuralEqual;
575
576impl<T: PartialEq> MutationPolicy<T> for StructuralEqual {
577 fn equivalent(&self, a: &T, b: &T) -> bool {
578 a == b
579 }
580}
581
582pub trait StateObject: Any {
583 fn object_id(&self) -> ObjectId;
584 fn first_record(&self) -> Rc<StateRecord>;
585 fn try_readable_record(
586 &self,
587 snapshot_id: SnapshotId,
588 invalid: &SnapshotIdSet,
589 ) -> Option<Rc<StateRecord>>;
590 fn readable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord>;
591
592 fn prepend_state_record(&self, record: Rc<StateRecord>);
593
594 fn merge_records(
595 &self,
596 _previous: Rc<StateRecord>,
597 _current: Rc<StateRecord>,
598 _applied: Rc<StateRecord>,
599 ) -> Option<Rc<StateRecord>> {
600 None
601 }
602
603 fn commit_merged_record(&self, _merged: Rc<StateRecord>) -> Result<SnapshotId, &'static str> {
604 Err("StateObject does not support merged record commits")
605 }
606 fn promote_record(&self, child_id: SnapshotId) -> Result<(), &'static str>;
607
608 fn overwrite_unused_records(&self) -> bool {
609 false
610 }
611
612 fn observation_lease(&self) -> Option<Rc<dyn Any>> {
613 None
614 }
615
616 fn as_any(&self) -> &dyn Any;
617}
618
619pub(crate) struct SnapshotMutableState<T> {
620 head: CurrentRecord,
621 policy: Arc<dyn MutationPolicy<T>>,
622 id: ObjectId,
623 weak_self: Mutex<Option<Weak<Self>>>,
624 apply_observers: ApplyObservers,
625 read_observation_lease: Rc<()>,
626 scope_observation_count: Cell<usize>,
627 subscriber_callbacks: RefCell<Vec<Rc<dyn Fn()>>>,
628}
629
630#[inline(always)]
631fn assert_record_chain(
632 head: &CurrentRecord,
633 id: ObjectId,
634 caller: &str,
635 snapshot_context: Option<SnapshotId>,
636) {
637 if should_check_chain_integrity() {
638 check_chain_integrity(&head.clone_head(), id, caller, snapshot_context);
639 }
640}
641
642enum Merge<'a, T> {
643 KeepCurrent,
644 Value(Option<T>),
645 Missing(&'static str, &'a Rc<StateRecord>),
646}
647
648type ApplyObservers = Mutex<Vec<Box<dyn Fn() + 'static>>>;
649
650fn lock_apply_observers(
651 observers: &ApplyObservers,
652) -> MutexGuard<'_, Vec<Box<dyn Fn() + 'static>>> {
653 observers.lock().unwrap_or_else(PoisonError::into_inner)
654}
655
656fn notify_applied(observers: &ApplyObservers) {
657 for observer in lock_apply_observers(observers).iter() {
658 observer();
659 }
660}
661
662fn finish_apply(
663 observers: &ApplyObservers,
664 head: &CurrentRecord,
665 id: ObjectId,
666 caller: &str,
667 snapshot_id: SnapshotId,
668) {
669 notify_applied(observers);
670 assert_record_chain(head, id, caller, Some(snapshot_id));
671}
672
673struct WriteTarget<'a> {
676 head: &'a CurrentRecord,
677 id: ObjectId,
678 state: &'a dyn StateObject,
679}
680
681struct GlobalWrite {
682 record: Rc<StateRecord>,
683 new_id: SnapshotId,
684 written: Option<Arc<dyn StateObject>>,
685}
686
687impl WriteTarget<'_> {
688 fn assert_chain_integrity(&self, caller: &str, snapshot_context: Option<SnapshotId>) {
689 assert_record_chain(self.head, self.id, caller, snapshot_context);
690 }
691
692 fn begin_global_write(
693 &self,
694 snapshot: &AnySnapshot,
695 global: &GlobalSnapshot,
696 snapshot_id: SnapshotId,
697 written: Option<Arc<dyn StateObject>>,
698 ) -> GlobalWrite {
699 assert!(
700 !global.has_pending_children(),
701 "SnapshotMutableState::set attempted global write while pending children {:?} exist (state {:?}, snapshot_id={})",
702 global.pending_children(),
703 self.id,
704 snapshot_id
705 );
706 if let Some(state) = &written {
707 snapshot.record_write(Arc::clone(state));
708 }
709 mark_update_write(self.id);
710 let new_id = allocate_record_id();
711 let record = new_overwritable_record_as_head_locked(self.state);
712 record.set_snapshot_id(new_id);
713 record.set_tombstone(false);
714 GlobalWrite {
715 record,
716 new_id,
717 written,
718 }
719 }
720
721 fn finish_global_write(
722 &self,
723 global: &GlobalSnapshot,
724 write: GlobalWrite,
725 snapshot_id: SnapshotId,
726 ) {
727 advance_global_snapshot(write.new_id);
728 self.assert_chain_integrity("set(global-push)", Some(snapshot_id));
729
730 if !global.has_pending_children() {
731 let mut cursor = write.record.next();
732 while let Some(node) = cursor {
733 if !node.is_tombstone() && node.snapshot_id() != PREEXISTING_SNAPSHOT_ID {
734 node.clear_value();
735 node.set_tombstone(true);
736 }
737 cursor = node.next();
738 }
739 self.assert_chain_integrity("set(global-tombstone)", Some(snapshot_id));
740 }
741
742 if let Some(modified) = write.written.as_ref() {
743 crate::snapshot_v2::notify_apply_observers(
744 std::slice::from_ref(modified),
745 write.new_id,
746 );
747 }
748 }
749
750 fn begin_child_write(
751 &self,
752 snapshot: &AnySnapshot,
753 snapshot_id: SnapshotId,
754 invalid: &SnapshotIdSet,
755 written: Option<Arc<dyn StateObject>>,
756 ) -> Rc<StateRecord> {
757 if let Some(state) = written {
758 snapshot.record_write(state);
759 }
760 mark_update_write(self.id);
761 self.writable_record(snapshot_id, invalid)
762 }
763
764 fn writable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord> {
765 let Some(readable) = readable_record_for(&self.head.clone_head(), snapshot_id, invalid)
766 else {
767 let current_head = self.head.clone_head();
768 let refreshed = readable_record_for(¤t_head, snapshot_id, invalid);
769 let source = refreshed.unwrap_or_else(|| current_head.clone());
770
771 let cloned_value = source
772 .cloned_value()
773 .unwrap_or_else(|| panic!("StateRecord value missing or wrong type"));
774 let new_head = StateRecord::with_boxed(snapshot_id, cloned_value, Some(current_head));
775 self.head.replace(new_head.clone());
776 self.assert_chain_integrity("writable_record(recover)", Some(snapshot_id));
777 return new_head;
778 };
779
780 if readable.snapshot_id() == snapshot_id {
781 return readable;
782 }
783
784 let refreshed = {
785 let current_head = self.head.clone_head();
786 let refreshed = readable_record_for(¤t_head, snapshot_id, invalid).unwrap_or_else(
787 || {
788 panic!(
789 "SnapshotMutableState::writable_record failed to locate refreshed readable record (state {:?}, snapshot_id={}, invalid={:?})",
790 self.id, snapshot_id, invalid
791 )
792 },
793 );
794
795 if refreshed.snapshot_id() == snapshot_id {
796 return refreshed;
797 }
798
799 Rc::clone(&refreshed)
800 };
801
802 let overwritable = new_overwritable_record_locked(self.state);
803 if let Err(error) = overwritable.assign_value(&refreshed) {
804 log::error!(
805 "snapshot writable record could not copy refreshed value for state {:?}: {:?}",
806 self.id,
807 error
808 );
809 }
810 overwritable.set_snapshot_id(snapshot_id);
811 overwritable.set_tombstone(false);
812
813 self.assert_chain_integrity("writable_record(reuse)", Some(snapshot_id));
814
815 overwritable
816 }
817}
818
819fn check_chain_integrity(
820 head: &Rc<StateRecord>,
821 id: ObjectId,
822 caller: &str,
823 snapshot_context: Option<SnapshotId>,
824) {
825 let mut cursor = Some(Rc::clone(head));
826 let mut seen: HashSet<usize> = HashSet::default();
827 let mut ids = Vec::new();
828
829 while let Some(record) = cursor {
830 let addr = Rc::as_ptr(&record) as usize;
831 assert!(
832 seen.insert(addr),
833 "SnapshotMutableState::{caller} detected duplicate/cycle at record {:p} for state {id:?} (snapshot_context={snapshot_context:?}, chain_ids={ids:?})",
834 Rc::as_ptr(&record),
835 );
836 ids.push(record.snapshot_id());
837 cursor = record.next();
838 }
839
840 assert!(
841 !ids.is_empty(),
842 "SnapshotMutableState::{caller} finished integrity scan with empty id list for state {id:?} (snapshot_context={snapshot_context:?})"
843 );
844}
845
846fn should_check_chain_integrity() -> bool {
847 #[cfg(debug_assertions)]
848 {
849 true
850 }
851
852 #[cfg(not(debug_assertions))]
853 {
854 crate::env_flag!("CRANPOSE_ASSERT_STATE_CHAIN")
855 }
856}
857
858impl<T: Clone + 'static> SnapshotMutableState<T> {
859 fn record_chain_debug(&self) -> Vec<(SnapshotId, bool)> {
860 let mut chain_ids = Vec::new();
861 let mut cursor = Some(self.first_record());
862 while let Some(record) = cursor {
863 chain_ids.push((record.snapshot_id(), record.is_tombstone()));
864 cursor = record.next();
865 }
866 chain_ids
867 }
868
869 fn readable_record_for_active_snapshot(&self) -> Result<Rc<StateRecord>, StateReadFailure> {
870 let snapshot = active_snapshot();
871 snapshot.record_read(self);
872
873 let snapshot_id = snapshot.snapshot_id();
874 if let Some(record) =
875 snapshot.with_invalid(|invalid| self.readable_for(snapshot_id, invalid))
876 {
877 return Ok(record);
878 }
879
880 let fresh_snapshot = active_snapshot();
881 let fresh_id = fresh_snapshot.snapshot_id();
882 let fresh_invalid = fresh_snapshot.invalid();
883
884 if let Some(record) = self.readable_for(fresh_id, &fresh_invalid) {
885 return Ok(record);
886 }
887
888 let global = GlobalSnapshot::get_or_create();
889 let global_id = global.snapshot_id();
890 let global_invalid = global.invalid();
891
892 if let Some(record) = self.readable_for(global_id, &global_invalid) {
893 return Ok(record);
894 }
895
896 Err(StateReadFailure {
897 state_id: self.id,
898 snapshot_id,
899 fresh_snapshot_id: fresh_id,
900 fresh_invalid,
901 record_chain: self.record_chain_debug(),
902 })
903 }
904
905 fn readable_for(
906 &self,
907 snapshot_id: SnapshotId,
908 invalid: &SnapshotIdSet,
909 ) -> Option<Rc<StateRecord>> {
910 let head = self.first_record();
911 readable_record_for(&head, snapshot_id, invalid)
912 }
913
914 fn is_equivalent_to(&self, readable: Option<Rc<StateRecord>>, new_value: &T) -> bool {
915 readable.is_some_and(|record| {
916 record.with_value(|current: &T| self.policy.equivalent(current, new_value))
917 })
918 }
919
920 pub(crate) fn new_in_arc(initial: T, policy: Arc<dyn MutationPolicy<T>>) -> Arc<Self> {
921 let snapshot = active_snapshot();
922 let snapshot_id = snapshot.snapshot_id();
923
924 let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, initial.clone(), None);
925 let head = StateRecord::new(snapshot_id, initial, Some(tail));
926
927 let mut state = Arc::new(Self {
928 head: CurrentRecord::new(head),
929 policy,
930 id: ObjectId::default(),
931 weak_self: Mutex::new(None),
932 apply_observers: Mutex::new(Vec::new()),
933 read_observation_lease: Rc::new(()),
934 scope_observation_count: Cell::new(0),
935 subscriber_callbacks: RefCell::new(Vec::new()),
936 });
937
938 let id = ObjectId::new(&state);
939 if let Some(state_inner) = Arc::get_mut(&mut state) {
940 state_inner.id = id;
941 }
942
943 *state.lock_weak_self() = Some(Arc::downgrade(&state));
944
945 state
946 }
947
948 pub(crate) fn add_apply_observer(&self, observer: Box<dyn Fn() + 'static>) {
949 self.lock_apply_observers().push(observer);
950 }
951
952 fn acquire_observation_lease(&self) -> Option<Rc<dyn Any>> {
953 let was_empty = !self.has_subscribers();
954 let lease = Rc::clone(&self.read_observation_lease);
955 if was_empty {
956 notify_subscriber_callbacks(&self.subscriber_callbacks);
957 }
958 Some(lease)
959 }
960
961 fn add_scope_observer(&self) -> bool {
962 let was_empty = !self.has_subscribers();
963 let count = self
964 .scope_observation_count
965 .get()
966 .checked_add(1)
967 .expect("state scope observation count overflow");
968 self.scope_observation_count.set(count);
969 was_empty
970 }
971
972 fn remove_scope_observers(&self, count: usize) {
973 if count == 0 {
974 return;
975 }
976 let remaining = self
977 .scope_observation_count
978 .get()
979 .checked_sub(count)
980 .expect("state scope observation count underflow");
981 self.scope_observation_count.set(remaining);
982 }
983
984 fn has_subscribers(&self) -> bool {
985 Rc::strong_count(&self.read_observation_lease) > 1 || self.scope_observation_count.get() > 0
986 }
987
988 fn subscriber_callback(&self, callback: Rc<dyn Fn()>, notify: bool) {
989 self.subscriber_callbacks
990 .borrow_mut()
991 .push(Rc::clone(&callback));
992 if notify {
993 callback();
994 }
995 }
996
997 fn notify_subscribers(&self) {
998 notify_subscriber_callbacks(&self.subscriber_callbacks);
999 }
1000
1001 fn lock_weak_self(&self) -> MutexGuard<'_, Option<Weak<Self>>> {
1002 self.weak_self
1003 .lock()
1004 .unwrap_or_else(PoisonError::into_inner)
1005 }
1006
1007 fn lock_apply_observers(&self) -> MutexGuard<'_, Vec<Box<dyn Fn() + 'static>>> {
1008 lock_apply_observers(&self.apply_observers)
1009 }
1010
1011 fn finish_apply(&self, caller: &str, snapshot_id: SnapshotId) {
1012 finish_apply(
1013 &self.apply_observers,
1014 &self.head,
1015 self.id,
1016 caller,
1017 snapshot_id,
1018 );
1019 }
1020
1021 fn upgrade_self(&self) -> Option<Arc<Self>> {
1022 self.lock_weak_self().as_ref().and_then(Weak::upgrade)
1023 }
1024
1025 fn written_state(&self) -> Option<Arc<dyn StateObject>> {
1026 self.upgrade_self()
1027 .map(|state| -> Arc<dyn StateObject> { state })
1028 }
1029
1030 #[inline]
1031 pub(crate) fn id(&self) -> ObjectId {
1032 self.id
1033 }
1034
1035 pub(crate) fn try_with_value<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1036 let record = self.readable_record_for_active_snapshot().ok()?;
1037 record.try_with_value(f)
1038 }
1039
1040 pub(crate) fn try_get(&self) -> Option<T> {
1041 self.try_with_value(Clone::clone)
1042 }
1043
1044 pub(crate) fn with_value<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1045 let record = self
1046 .readable_record_for_active_snapshot()
1047 .unwrap_or_else(|failure| panic!("{failure}"));
1048 record.with_value(f)
1049 }
1050
1051 pub(crate) fn get(&self) -> T {
1052 self.with_value(Clone::clone)
1053 }
1054
1055 pub(crate) fn set(&self, new_value: T) -> bool {
1056 #[cfg(debug_assertions)]
1057 {
1058 let in_handler = crate::in_event_handler();
1059 let in_snapshot = crate::in_applied_snapshot();
1060 if in_handler && !in_snapshot {
1061 log::warn!(
1062 target: "cranpose::state",
1063 "State modified in event handler without run_in_mutable_snapshot; \
1064 this can make updates invisible to other contexts. Wrap the handler \
1065 in run_in_mutable_snapshot() or dispatch_ui_event(). State: {:?}",
1066 self.id
1067 );
1068 }
1069 }
1070
1071 let snapshot = active_snapshot();
1072 let snapshot_id = snapshot.snapshot_id();
1073 let target = WriteTarget {
1074 head: &self.head,
1075 id: self.id,
1076 state: self,
1077 };
1078
1079 match &snapshot {
1080 AnySnapshot::Global(global) => {
1081 let readable =
1082 snapshot.with_invalid(|invalid| self.readable_for(snapshot_id, invalid));
1083 if self.is_equivalent_to(readable, &new_value) {
1084 return false;
1085 }
1086 let write =
1087 target.begin_global_write(&snapshot, global, snapshot_id, self.written_state());
1088 write.record.replace_value(new_value);
1089 target.finish_global_write(global, write, snapshot_id);
1090 }
1091 AnySnapshot::Mutable(_)
1092 | AnySnapshot::NestedMutable(_)
1093 | AnySnapshot::TransparentMutable(_) => {
1094 let invalid = snapshot.invalid();
1095 if self.is_equivalent_to(self.readable_for(snapshot_id, &invalid), &new_value) {
1096 return false;
1097 }
1098 let record = target.begin_child_write(
1099 &snapshot,
1100 snapshot_id,
1101 &invalid,
1102 self.written_state(),
1103 );
1104 record.replace_value(new_value);
1105 target.assert_chain_integrity("set(child-writable)", Some(snapshot_id));
1106 }
1107 AnySnapshot::Readonly(_)
1108 | AnySnapshot::NestedReadonly(_)
1109 | AnySnapshot::TransparentReadonly(_) => {
1110 panic!("Cannot write to a read-only snapshot");
1111 }
1112 }
1113
1114 true
1115 }
1116}
1117
1118thread_local! {
1119 static ACTIVE_UPDATES: RefCell<HashSet<ObjectId>> = RefCell::new(HashSet::default());
1120 static PENDING_WRITES: RefCell<HashSet<ObjectId>> = RefCell::new(HashSet::default());
1121}
1122
1123pub(crate) struct UpdateScope {
1124 id: ObjectId,
1125 finished: bool,
1126}
1127
1128impl UpdateScope {
1129 pub(crate) fn new(id: ObjectId) -> Self {
1130 ACTIVE_UPDATES.with(|active| {
1131 active.borrow_mut().insert(id);
1132 });
1133 PENDING_WRITES.with(|pending| {
1134 pending.borrow_mut().remove(&id);
1135 });
1136 Self {
1137 id,
1138 finished: false,
1139 }
1140 }
1141
1142 pub(crate) fn finish(mut self) -> bool {
1143 self.finished = true;
1144 ACTIVE_UPDATES.with(|active| {
1145 active.borrow_mut().remove(&self.id);
1146 });
1147 PENDING_WRITES.with(|pending| pending.borrow_mut().remove(&self.id))
1148 }
1149}
1150
1151impl Drop for UpdateScope {
1152 fn drop(&mut self) {
1153 if self.finished {
1154 return;
1155 }
1156 ACTIVE_UPDATES.with(|active| {
1157 active.borrow_mut().remove(&self.id);
1158 });
1159 PENDING_WRITES.with(|pending| {
1160 pending.borrow_mut().remove(&self.id);
1161 });
1162 }
1163}
1164
1165fn mark_update_write(id: ObjectId) {
1166 ACTIVE_UPDATES.with(|active| {
1167 if active.borrow().contains(&id) {
1168 PENDING_WRITES.with(|pending| {
1169 pending.borrow_mut().insert(id);
1170 });
1171 }
1172 });
1173}
1174
1175impl<T: Clone + 'static> SnapshotMutableState<T> {
1176 fn try_readable_record(
1177 &self,
1178 snapshot_id: SnapshotId,
1179 invalid: &SnapshotIdSet,
1180 ) -> Option<Rc<StateRecord>> {
1181 self.readable_for(snapshot_id, invalid)
1182 }
1183}
1184
1185impl<T: Clone + 'static> StateObject for SnapshotMutableState<T> {
1186 fn object_id(&self) -> ObjectId {
1187 self.id
1188 }
1189
1190 fn first_record(&self) -> Rc<StateRecord> {
1191 self.head.clone_head()
1192 }
1193
1194 fn try_readable_record(
1195 &self,
1196 snapshot_id: SnapshotId,
1197 invalid: &SnapshotIdSet,
1198 ) -> Option<Rc<StateRecord>> {
1199 self.try_readable_record(snapshot_id, invalid)
1200 }
1201
1202 fn readable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord> {
1203 self.try_readable_record(snapshot_id, invalid)
1204 .unwrap_or_else(|| {
1205 panic!(
1206 "SnapshotMutableState::readable_record returned null (state={:?}, snapshot_id={})",
1207 self.id, snapshot_id
1208 )
1209 })
1210 }
1211
1212 fn prepend_state_record(&self, record: Rc<StateRecord>) {
1213 self.head.prepend(record);
1214 }
1215
1216 fn observation_lease(&self) -> Option<Rc<dyn Any>> {
1217 self.acquire_observation_lease()
1218 }
1219
1220 fn merge_records(
1221 &self,
1222 previous: Rc<StateRecord>,
1223 current: Rc<StateRecord>,
1224 applied: Rc<StateRecord>,
1225 ) -> Option<Rc<StateRecord>> {
1226 let outcome = current
1227 .try_with_value(|current_value: &T| {
1228 applied
1229 .try_with_value(|applied_value: &T| {
1230 if self.policy.equivalent(current_value, applied_value) {
1231 return Merge::KeepCurrent;
1232 }
1233 previous
1234 .try_with_value(|previous_value: &T| {
1235 Merge::Value(self.policy.merge(
1236 previous_value,
1237 current_value,
1238 applied_value,
1239 ))
1240 })
1241 .unwrap_or(Merge::Missing("previous", &previous))
1242 })
1243 .unwrap_or(Merge::Missing("applied", &applied))
1244 })
1245 .unwrap_or(Merge::Missing("current", ¤t));
1246 match outcome {
1247 Merge::KeepCurrent => Some(current),
1248 Merge::Value(merged) => {
1249 merged.map(|value| StateRecord::new(applied.snapshot_id(), value, None))
1250 }
1251 Merge::Missing(which, record) => {
1252 log_merge_value_missing(which, self.id, record);
1253 None
1254 }
1255 }
1256 }
1257
1258 fn promote_record(&self, child_id: SnapshotId) -> Result<(), &'static str> {
1259 promote_record_locked(self, child_id, TypeId::of::<T>())?;
1260 self.finish_apply("promote_record", child_id);
1261 Ok(())
1262 }
1263
1264 fn commit_merged_record(&self, merged: Rc<StateRecord>) -> Result<SnapshotId, &'static str> {
1265 let new_id = commit_merged_record_locked(self, &merged, TypeId::of::<T>())?;
1266 self.finish_apply("commit_merged_record", new_id);
1267 Ok(new_id)
1268 }
1269
1270 fn overwrite_unused_records(&self) -> bool {
1271 overwrite_unused_records_locked(self)
1272 }
1273
1274 fn as_any(&self) -> &dyn Any {
1275 self
1276 }
1277}
1278
1279pub(crate) struct MutableStateInner<T: Clone + 'static> {
1280 pub(crate) state: Arc<SnapshotMutableState<T>>,
1281 pub(crate) watchers: RefCell<HashMap<ScopeId, RcWeak<RecomposeScopeInner>>>,
1282 runtime: RuntimeHandle,
1283 state_id: Cell<Option<StateId>>,
1284}
1285
1286fn notify_subscriber_callbacks(callbacks: &RefCell<Vec<Rc<dyn Fn()>>>) {
1287 let registered = callbacks.borrow().len();
1288 for index in 0..registered {
1289 let callback = callbacks.borrow().get(index).map(Rc::clone);
1290 if let Some(callback) = callback {
1291 callback();
1292 }
1293 }
1294}
1295
1296fn shrink_watchers_if_sparse(watchers: &mut HashMap<ScopeId, RcWeak<RecomposeScopeInner>>) {
1297 let len = watchers.len();
1298 let capacity = watchers.capacity();
1299 if capacity > len.saturating_mul(4).max(32) {
1300 watchers.shrink_to_fit();
1301 }
1302}
1303
1304impl<T: Clone + 'static> MutableStateInner<T> {
1305 pub(crate) fn new_with_policy(
1306 value: T,
1307 runtime: RuntimeHandle,
1308 policy: Arc<dyn MutationPolicy<T>>,
1309 ) -> Self {
1310 Self {
1311 state: SnapshotMutableState::new_in_arc(value, policy),
1312 watchers: RefCell::new(HashMap::default()),
1313 runtime,
1314 state_id: Cell::new(None),
1315 }
1316 }
1317
1318 pub(crate) fn install_snapshot_observer(&self, state_id: StateId) {
1319 self.state_id.set(Some(state_id));
1320 let runtime_handle = self.runtime.clone();
1321 self.state.add_apply_observer(Box::new(move || {
1322 let runtime = runtime_handle.clone();
1323 runtime_handle.enqueue_ui_task(Box::new(move || {
1324 runtime.with_state_arena(|arena| {
1325 let _ = arena.with_typed_opt::<T, _>(state_id, |inner| {
1326 inner.invalidate_watchers();
1327 });
1328 });
1329 }));
1330 }));
1331 }
1332
1333 fn register_scope(&self, scope: &RecomposeScope) -> (bool, bool) {
1343 let mut watchers = self.watchers.borrow_mut();
1344 let id = scope.id();
1345 match watchers.get(&id) {
1346 Some(existing) if existing.strong_count() > 0 => return (false, false),
1347 Some(_) => {
1348 watchers.remove(&id);
1349 self.state.remove_scope_observers(1);
1350 }
1351 None => {}
1352 }
1353 let any_live = watchers.values().any(|watcher| watcher.strong_count() > 0);
1354 if !any_live || watchers.len() >= watchers.capacity() {
1355 let before = watchers.len();
1356 watchers.retain(|_, watcher| watcher.strong_count() > 0);
1357 self.state.remove_scope_observers(before - watchers.len());
1358 }
1359 watchers.insert(id, scope.downgrade());
1360 drop(watchers);
1361 (true, self.state.add_scope_observer())
1362 }
1363
1364 fn has_subscribers(&self) -> bool {
1365 let mut watchers = self.watchers.borrow_mut();
1366 let before = watchers.len();
1367 watchers.retain(|_, existing| existing.upgrade().is_some());
1368 self.state.remove_scope_observers(before - watchers.len());
1369 self.state.has_subscribers()
1370 }
1371
1372 pub(crate) fn unregister_scope(&self, scope_id: ScopeId) {
1373 let mut watchers = self.watchers.borrow_mut();
1374 let removed = if watchers
1375 .get(&scope_id)
1376 .is_some_and(|weak| weak.upgrade().is_none())
1377 {
1378 watchers.remove(&scope_id);
1379 shrink_watchers_if_sparse(&mut watchers);
1380 true
1381 } else {
1382 false
1383 };
1384 drop(watchers);
1385 self.state.remove_scope_observers(usize::from(removed));
1386 }
1387
1388 fn state_id(&self) -> Option<StateId> {
1389 self.state_id.get()
1390 }
1391
1392 fn invalidate_watchers(&self) {
1393 let (watchers, removed_count): (Vec<RecomposeScope>, usize) = {
1394 let mut watchers = self.watchers.borrow_mut();
1395 let before = watchers.len();
1396 let mut live = Vec::with_capacity(watchers.len());
1397 watchers.retain(|_, scope| {
1398 if let Some(inner) = scope.upgrade() {
1399 live.push(RecomposeScope { inner });
1400 true
1401 } else {
1402 false
1403 }
1404 });
1405 let removed_count = before - watchers.len();
1406 shrink_watchers_if_sparse(&mut watchers);
1407 (live, removed_count)
1408 };
1409 self.state.remove_scope_observers(removed_count);
1410
1411 for watcher in watchers {
1412 debug_record_scope_invalidation(
1413 watcher.id(),
1414 self.state_id.get(),
1415 std::any::type_name::<T>(),
1416 );
1417 if let Some(state_id) = self.state_id.get() {
1418 watcher.invalidate_from_state(state_id);
1419 } else {
1420 watcher.invalidate();
1421 }
1422 }
1423 }
1424}
1425
1426impl<T: Clone + 'static> Drop for MutableStateInner<T> {
1427 fn drop(&mut self) {
1428 self.state
1429 .remove_scope_observers(self.watchers.get_mut().len());
1430 }
1431}
1432
1433fn register_current_state_scope<T: Clone + 'static>(inner: &MutableStateInner<T>) {
1434 let Some(Some(scope)) =
1435 with_current_composer_opt(super::composer::Composer::current_state_invalidation_scope)
1436 else {
1437 return;
1438 };
1439 let (registered, became_subscribed) = inner.register_scope(&scope);
1440 if registered {
1441 if let Some(state_id) = inner.state_id() {
1442 scope.record_state_subscription(state_id);
1443 }
1444 if became_subscribed {
1445 inner.state.notify_subscribers();
1446 }
1447 }
1448}
1449
1450trait StateArenaHandle<T: Clone + 'static> {
1451 fn state_id(&self) -> StateId;
1452 fn runtime_id(&self) -> runtime::RuntimeId;
1453
1454 fn runtime_handle(&self) -> RuntimeHandle {
1455 runtime::runtime_handle_by_id(self.runtime_id())
1456 .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
1457 }
1458
1459 fn runtime_handle_opt(&self) -> Option<RuntimeHandle> {
1460 runtime::runtime_handle_by_id(self.runtime_id())
1461 }
1462
1463 fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1464 runtime::with_state_arena_by_id(self.runtime_id(), |arena| {
1465 arena.with_typed::<T, R>(self.state_id(), f)
1466 })
1467 .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
1468 }
1469
1470 fn try_with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
1471 runtime::with_state_arena_by_id(self.runtime_id(), |arena| {
1472 arena.with_typed_opt::<T, R>(self.state_id(), f)
1473 })?
1474 }
1475
1476 fn read_subscribed<R>(&self, read: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1477 self.with_inner(|inner| {
1478 let result = read(inner);
1479 register_current_state_scope(inner);
1480 result
1481 })
1482 }
1483}
1484
1485pub struct State<T: Clone + 'static> {
1487 id: StateId,
1488 runtime_id: runtime::RuntimeId,
1489 _marker: PhantomData<fn() -> T>,
1490}
1491
1492pub struct MutableState<T: Clone + 'static> {
1498 id: StateId,
1499 runtime_id: runtime::RuntimeId,
1500 _marker: PhantomData<fn() -> T>,
1501}
1502
1503#[derive(Clone)]
1505pub struct OwnedMutableState<T: Clone + 'static> {
1506 state: MutableState<T>,
1507 _lease: Rc<runtime::StateHandleLease>,
1508 _marker: PhantomData<fn() -> T>,
1509}
1510
1511impl<T: Clone + 'static> PartialEq for State<T> {
1512 fn eq(&self, other: &Self) -> bool {
1513 self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
1514 }
1515}
1516
1517impl<T: Clone + 'static> Eq for State<T> {}
1518
1519impl<T: Clone + 'static> PartialEq for MutableState<T> {
1520 fn eq(&self, other: &Self) -> bool {
1521 self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
1522 }
1523}
1524
1525impl<T: Clone + 'static> Eq for MutableState<T> {}
1526
1527impl<T: Clone + 'static> Copy for State<T> {}
1528
1529impl<T: Clone + 'static> Clone for State<T> {
1530 fn clone(&self) -> Self {
1531 *self
1532 }
1533}
1534
1535impl<T: Clone + 'static> Copy for MutableState<T> {}
1536
1537impl<T: Clone + 'static> Clone for MutableState<T> {
1538 fn clone(&self) -> Self {
1539 *self
1540 }
1541}
1542
1543impl<T: Clone + 'static> StateArenaHandle<T> for State<T> {
1544 fn state_id(&self) -> StateId {
1545 self.id
1546 }
1547
1548 fn runtime_id(&self) -> runtime::RuntimeId {
1549 self.runtime_id
1550 }
1551}
1552
1553impl<T: Clone + 'static> State<T> {
1554 pub fn is_alive(&self) -> bool {
1555 self.try_with_inner(|_| ()).is_some()
1556 }
1557
1558 pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1559 self.try_with_inner(|inner| inner.state.try_with_value(f))?
1560 }
1561
1562 pub fn try_value(&self) -> Option<T> {
1563 self.try_with_inner(|inner| inner.state.try_get())?
1564 }
1565
1566 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1570 let value = self.read_subscribed(|inner| inner.state.get());
1571 f(&value)
1572 }
1573
1574 pub fn read<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1579 self.read_subscribed(|inner| inner.state.with_value(f))
1580 }
1581
1582 pub fn value(&self) -> T {
1583 self.read_subscribed(|inner| inner.state.get())
1584 }
1585
1586 pub fn get(&self) -> T {
1587 self.value()
1588 }
1589
1590 pub fn has_subscribers(&self) -> bool {
1591 self.with_inner(MutableStateInner::has_subscribers)
1592 }
1593
1594 pub fn on_subscriber(&self, callback: impl Fn() + 'static) {
1600 let callback: Rc<dyn Fn()> = Rc::new(callback);
1601 self.with_inner(|inner| {
1602 inner
1603 .state
1604 .subscriber_callback(callback, inner.has_subscribers());
1605 });
1606 }
1607
1608 pub fn subscription_hold(&self) -> StateSubscriptionHold {
1620 StateSubscriptionHold {
1621 _lease: self
1622 .try_with_inner(|inner| inner.state.observation_lease())
1623 .flatten(),
1624 }
1625 }
1626}
1627
1628pub struct StateSubscriptionHold {
1631 _lease: Option<Rc<dyn Any>>,
1632}
1633
1634impl<T: Clone + 'static> StateArenaHandle<T> for MutableState<T> {
1635 fn state_id(&self) -> StateId {
1636 self.id
1637 }
1638
1639 fn runtime_id(&self) -> runtime::RuntimeId {
1640 self.runtime_id
1641 }
1642}
1643
1644impl<T: Clone + 'static> MutableState<T> {
1645 pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
1646 runtime.alloc_persistent_state(value)
1647 }
1648
1649 fn from_parts(id: StateId, runtime_id: runtime::RuntimeId) -> Self {
1650 Self {
1651 id,
1652 runtime_id,
1653 _marker: PhantomData,
1654 }
1655 }
1656
1657 pub(crate) fn from_lease(lease: &Rc<runtime::StateHandleLease>) -> Self {
1658 Self::from_parts(lease.id(), lease.runtime().id())
1659 }
1660
1661 pub fn is_alive(&self) -> bool {
1662 self.try_with_inner(|_| ()).is_some()
1663 }
1664
1665 pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1666 self.try_with_inner(|inner| inner.state.try_with_value(f))?
1667 }
1668
1669 pub fn try_value(&self) -> Option<T> {
1670 self.try_with_inner(|inner| inner.state.try_get())?
1671 }
1672
1673 pub fn as_state(&self) -> State<T> {
1674 State {
1675 id: self.id,
1676 runtime_id: self.runtime_id,
1677 _marker: PhantomData,
1678 }
1679 }
1680
1681 pub fn try_retain(&self) -> Option<OwnedMutableState<T>> {
1682 let lease = self
1683 .runtime_handle_opt()?
1684 .retain_state_lease(self.state_id())?;
1685 Some(OwnedMutableState {
1686 state: *self,
1687 _lease: lease,
1688 _marker: PhantomData,
1689 })
1690 }
1691
1692 pub fn retain(&self) -> OwnedMutableState<T> {
1693 self.try_retain()
1694 .unwrap_or_else(|| panic!("state {:?} is no longer alive", self.state_id()))
1695 }
1696
1697 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1701 let value = self.read_subscribed(|inner| inner.state.get());
1702 f(&value)
1703 }
1704
1705 pub fn read<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1710 self.read_subscribed(|inner| inner.state.with_value(f))
1711 }
1712
1713 pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
1714 let runtime = self.runtime_handle();
1715 runtime.assert_ui_thread();
1716 runtime.with_state_arena(|arena| {
1717 arena.with_typed::<T, R>(self.state_id(), |inner| {
1718 let mut value = inner.state.get();
1719 let tracker = UpdateScope::new(inner.state.id());
1720 let result = f(&mut value);
1721 let wrote_elsewhere = tracker.finish();
1722 if !wrote_elsewhere && inner.state.set(value) {
1723 inner.invalidate_watchers();
1724 }
1725 result
1726 })
1727 })
1728 }
1729
1730 pub fn replace(&self, value: T) {
1731 let Some(runtime) = self.runtime_handle_opt() else {
1732 log::debug!(
1733 "MutableState::replace skipped: runtime {:?} dropped",
1734 self.runtime_id()
1735 );
1736 return;
1737 };
1738 runtime.assert_ui_thread();
1739 let replaced = runtime
1740 .try_with_state_arena(|arena| {
1741 arena.with_typed_opt::<T, ()>(self.state_id(), |inner| {
1742 if inner.state.set(value) {
1743 inner.invalidate_watchers();
1744 }
1745 })
1746 })
1747 .flatten();
1748 if replaced.is_none() {
1749 log::debug!(
1750 "MutableState::replace skipped: state cell released (slot={}, gen={})",
1751 self.state_id().slot(),
1752 self.state_id().generation(),
1753 );
1754 }
1755 }
1756
1757 pub fn set_value(&self, value: T) {
1758 self.replace(value);
1759 }
1760
1761 pub fn set(&self, value: T) {
1762 self.replace(value);
1763 }
1764
1765 pub fn value(&self) -> T {
1766 self.read_subscribed(|inner| inner.state.get())
1767 }
1768
1769 pub fn get(&self) -> T {
1770 self.value()
1771 }
1772
1773 pub fn get_non_reactive(&self) -> T {
1774 self.with_inner(|inner| inner.state.get())
1775 }
1776
1777 #[doc(hidden)]
1778 pub fn runtime_state_id(&self) -> StateId {
1779 self.state_id()
1780 }
1781
1782 #[doc(hidden)]
1783 pub fn subscribe_current_scope_only(&self) {
1784 self.subscribe_current_scope();
1785 }
1786
1787 fn subscribe_current_scope(&self) {
1788 self.with_inner(register_current_state_scope::<T>);
1789 }
1790
1791 #[cfg(test)]
1792 pub(crate) fn watcher_count(&self) -> usize {
1793 self.with_inner(|inner| inner.watchers.borrow().len())
1794 }
1795
1796 #[cfg(test)]
1797 pub(crate) fn watcher_capacity(&self) -> usize {
1798 self.with_inner(|inner| inner.watchers.borrow().capacity())
1799 }
1800
1801 #[cfg(test)]
1802 pub(crate) fn state_id_for_test(&self) -> StateId {
1803 self.state_id()
1804 }
1805
1806 #[cfg(test)]
1807 pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
1808 self.as_state().subscribe_scope_for_test(scope);
1809 }
1810}
1811
1812impl<T: Clone + 'static> OwnedMutableState<T> {
1813 pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
1814 let lease = runtime.alloc_state(value);
1815 Self {
1816 state: MutableState::from_lease(&lease),
1817 _lease: lease,
1818 _marker: PhantomData,
1819 }
1820 }
1821
1822 pub fn with_runtime_structural_eq(value: T, runtime: RuntimeHandle) -> Self
1823 where
1824 T: PartialEq,
1825 {
1826 Self::with_runtime_and_policy(value, runtime, Arc::new(StructuralEqual))
1827 }
1828
1829 pub(crate) fn with_runtime_and_policy(
1830 value: T,
1831 runtime: RuntimeHandle,
1832 policy: Arc<dyn MutationPolicy<T>>,
1833 ) -> Self {
1834 let lease = runtime.alloc_state_with_policy(value, policy);
1835 Self {
1836 state: MutableState::from_lease(&lease),
1837 _lease: lease,
1838 _marker: PhantomData,
1839 }
1840 }
1841
1842 pub fn handle(&self) -> MutableState<T> {
1843 self.state
1844 }
1845
1846 pub fn as_state(&self) -> State<T> {
1847 self.state.as_state()
1848 }
1849}
1850
1851impl<T: Clone + 'static> Deref for OwnedMutableState<T> {
1852 type Target = MutableState<T>;
1853
1854 fn deref(&self) -> &Self::Target {
1855 &self.state
1856 }
1857}
1858
1859#[cfg(test)]
1860impl<T: Clone + 'static> State<T> {
1861 pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
1862 self.with_inner(|inner| {
1863 let (registered, became_subscribed) = inner.register_scope(scope);
1864 if registered {
1865 if let Some(state_id) = inner.state_id() {
1866 scope.record_state_subscription(state_id);
1867 }
1868 if became_subscribed {
1869 inner.state.notify_subscribers();
1870 }
1871 }
1872 });
1873 }
1874}
1875
1876impl<T: fmt::Debug + Clone + 'static> fmt::Debug for MutableState<T> {
1877 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1878 if let Some(value) = self.try_value() {
1879 f.debug_struct("MutableState")
1880 .field("value", &value)
1881 .finish()
1882 } else {
1883 f.write_str("MutableState { value: <unavailable> }")
1884 }
1885 }
1886}
1887
1888#[derive(Clone)]
1889pub struct SnapshotStateList<T: Clone + 'static> {
1890 state: OwnedMutableState<Vec<T>>,
1891}
1892
1893impl<T: Clone + 'static> SnapshotStateList<T> {
1894 pub fn with_runtime<I>(values: I, runtime: RuntimeHandle) -> Self
1895 where
1896 I: IntoIterator<Item = T>,
1897 {
1898 let initial: Vec<T> = values.into_iter().collect();
1899 Self {
1900 state: OwnedMutableState::with_runtime(initial, runtime),
1901 }
1902 }
1903
1904 pub fn as_state(&self) -> State<Vec<T>> {
1905 self.state.as_state()
1906 }
1907
1908 pub fn as_mutable_state(&self) -> MutableState<Vec<T>> {
1909 self.state.handle()
1910 }
1911
1912 pub fn len(&self) -> usize {
1913 self.state.with(Vec::len)
1914 }
1915
1916 pub fn is_empty(&self) -> bool {
1917 self.len() == 0
1918 }
1919
1920 pub fn to_vec(&self) -> Vec<T> {
1921 self.state.with(Clone::clone)
1922 }
1923
1924 pub fn iter(&self) -> Vec<T> {
1925 self.to_vec()
1926 }
1927
1928 pub fn get(&self, index: usize) -> T {
1929 self.state.with(|values| values[index].clone())
1930 }
1931
1932 pub fn get_opt(&self, index: usize) -> Option<T> {
1933 self.state.with(|values| values.get(index).cloned())
1934 }
1935
1936 pub fn first(&self) -> Option<T> {
1937 self.get_opt(0)
1938 }
1939
1940 pub fn last(&self) -> Option<T> {
1941 self.state.with(|values| values.last().cloned())
1942 }
1943
1944 pub fn push(&self, value: T) {
1945 self.state.update(|values| values.push(value));
1946 }
1947
1948 pub fn extend<I>(&self, iter: I)
1949 where
1950 I: IntoIterator<Item = T>,
1951 {
1952 self.state.update(|values| values.extend(iter));
1953 }
1954
1955 pub fn insert(&self, index: usize, value: T) {
1956 self.state.update(|values| values.insert(index, value));
1957 }
1958
1959 pub fn set(&self, index: usize, value: T) -> T {
1960 self.state
1961 .update(|values| std::mem::replace(&mut values[index], value))
1962 }
1963
1964 pub fn remove(&self, index: usize) -> T {
1965 self.state.update(|values| values.remove(index))
1966 }
1967
1968 pub fn pop(&self) -> Option<T> {
1969 self.state.update(Vec::pop)
1970 }
1971
1972 pub fn clear(&self) {
1973 self.state.replace(Vec::new());
1974 }
1975
1976 pub fn retain<F>(&self, mut predicate: F)
1977 where
1978 F: FnMut(&T) -> bool,
1979 {
1980 self.state
1981 .update(|values| values.retain(|value| predicate(value)));
1982 }
1983
1984 pub fn replace_with<I>(&self, iter: I)
1985 where
1986 I: IntoIterator<Item = T>,
1987 {
1988 self.state.replace(iter.into_iter().collect());
1989 }
1990}
1991
1992impl<T: fmt::Debug + Clone + 'static> fmt::Debug for SnapshotStateList<T> {
1993 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1994 let contents = self.to_vec();
1995 f.debug_struct("SnapshotStateList")
1996 .field("values", &contents)
1997 .finish()
1998 }
1999}
2000
2001#[derive(Clone)]
2002pub struct SnapshotStateMap<K, V>
2003where
2004 K: Clone + Eq + Hash + 'static,
2005 V: Clone + 'static,
2006{
2007 state: OwnedMutableState<HashMap<K, V>>,
2008}
2009
2010impl<K, V> SnapshotStateMap<K, V>
2011where
2012 K: Clone + Eq + Hash + 'static,
2013 V: Clone + 'static,
2014{
2015 pub fn with_runtime<I>(pairs: I, runtime: RuntimeHandle) -> Self
2016 where
2017 I: IntoIterator<Item = (K, V)>,
2018 {
2019 let map: HashMap<K, V> = pairs.into_iter().collect();
2020 Self {
2021 state: OwnedMutableState::with_runtime(map, runtime),
2022 }
2023 }
2024
2025 pub fn as_state(&self) -> State<HashMap<K, V>> {
2026 self.state.as_state()
2027 }
2028
2029 pub fn as_mutable_state(&self) -> MutableState<HashMap<K, V>> {
2030 self.state.handle()
2031 }
2032
2033 pub fn len(&self) -> usize {
2034 self.state.with(std::collections::HashMap::len)
2035 }
2036
2037 pub fn is_empty(&self) -> bool {
2038 self.state.with(std::collections::HashMap::is_empty)
2039 }
2040
2041 pub fn contains_key(&self, key: &K) -> bool {
2042 self.state.with(|map| map.contains_key(key))
2043 }
2044
2045 pub fn get(&self, key: &K) -> Option<V> {
2046 self.state.with(|map| map.get(key).cloned())
2047 }
2048
2049 pub fn to_hash_map(&self) -> HashMap<K, V> {
2050 self.state.with(Clone::clone)
2051 }
2052
2053 pub fn insert(&self, key: K, value: V) -> Option<V> {
2054 self.state.update(|map| map.insert(key, value))
2055 }
2056
2057 pub fn extend<I>(&self, iter: I)
2058 where
2059 I: IntoIterator<Item = (K, V)>,
2060 {
2061 self.state.update(|map| map.extend(iter));
2062 }
2063
2064 pub fn remove(&self, key: &K) -> Option<V> {
2065 self.state.update(|map| map.remove(key))
2066 }
2067
2068 pub fn clear(&self) {
2069 self.state.replace(HashMap::default());
2070 }
2071
2072 pub fn retain<F>(&self, mut predicate: F)
2073 where
2074 F: FnMut(&K, &mut V) -> bool,
2075 {
2076 self.state.update(|map| map.retain(|k, v| predicate(k, v)));
2077 }
2078}
2079
2080impl<K, V> fmt::Debug for SnapshotStateMap<K, V>
2081where
2082 K: Clone + Eq + Hash + fmt::Debug + 'static,
2083 V: Clone + fmt::Debug + 'static,
2084{
2085 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2086 let contents = self.to_hash_map();
2087 f.debug_struct("SnapshotStateMap")
2088 .field("entries", &contents)
2089 .finish()
2090 }
2091}
2092
2093pub(crate) struct DerivedState<T: Clone + PartialEq + 'static> {
2094 compute: Rc<dyn Fn() -> T>,
2095 pub(crate) state: OwnedMutableState<T>,
2096}
2097
2098impl<T: Clone + PartialEq + 'static> DerivedState<T> {
2099 pub(crate) fn new(runtime: RuntimeHandle, compute: Rc<dyn Fn() -> T>) -> Self {
2100 let initial = compute();
2101 Self {
2102 compute,
2103 state: OwnedMutableState::with_runtime_structural_eq(initial, runtime),
2104 }
2105 }
2106
2107 pub(crate) fn set_compute(&mut self, compute: Rc<dyn Fn() -> T>) {
2108 self.compute = compute;
2109 }
2110
2111 pub(crate) fn recompute(&self) {
2112 let value = (self.compute)();
2113 self.state.set_value(value);
2114 }
2115}
2116
2117impl<T: fmt::Debug + Clone + 'static> fmt::Debug for State<T> {
2118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2119 if let Some(value) = self.try_value() {
2120 f.debug_struct("State").field("value", &value).finish()
2121 } else {
2122 f.write_str("State { value: <unavailable> }")
2123 }
2124 }
2125}
2126
2127#[cfg(test)]
2128#[path = "tests/state_tests.rs"]
2129mod tests;