1use std::{
2 any::{Any, TypeId},
3 cell::{Cell, OnceCell, RefCell},
4 fmt,
5 hash::Hash,
6 marker::PhantomData,
7 ops::Deref,
8 rc::{Rc, Weak},
9};
10
11use smallvec::{SmallVec, smallvec};
12
13use crate::{
14 RecomposeScope, RecomposeScopeInner, RuntimeHandle, ScopeId, StateId,
15 collections::map::HashMap,
16 debug_trace::debug_record_scope_invalidation,
17 runtime,
18 snapshot_id_set::{SnapshotId, SnapshotIdSet},
19 snapshot_v2::{
20 AnySnapshot, GlobalSnapshot, advance_global_snapshot, allocate_record_id, current_snapshot,
21 },
22 with_current_composer_opt,
23};
24
25mod derived_state;
26mod readable_record;
27mod record_chain;
28mod snapshot_state_list;
29
30pub(crate) use derived_state::DerivedState;
31use readable_record::{ReadNotice, StateReadFailure, readable_record_for_active_snapshot};
32pub(crate) use record_chain::RecordChain;
33pub use snapshot_state_list::SnapshotStateList;
34
35pub(crate) const PREEXISTING_SNAPSHOT_ID: SnapshotId = SnapshotId(1);
36
37const INVALID_SNAPSHOT_ID: SnapshotId = SnapshotId(0);
38
39const SNAPSHOT_ID_MAX: SnapshotId = SnapshotId(usize::MAX);
40
41#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, Default)]
42pub struct ObjectId(pub(crate) usize);
43
44impl ObjectId {
45 #[cfg(test)]
46 pub(crate) fn new<T: ?Sized + 'static>(object: &Rc<T>) -> Self {
47 Self(Rc::as_ptr(object) as *const () as usize)
48 }
49}
50
51pub struct StateRecord {
52 snapshot_id: Cell<SnapshotId>,
53 tombstone: Cell<bool>,
54 value: RefCell<Option<Box<dyn RecordValue>>>,
55}
56
57pub(crate) trait RecordValue: Any {
58 fn clone_boxed(&self) -> Box<dyn RecordValue>;
59 fn assign_into(&self, target: &mut Option<Box<dyn RecordValue>>);
60 fn holds_resources(&self) -> bool;
62}
63
64impl<T: Any + Clone> RecordValue for T {
65 fn holds_resources(&self) -> bool {
66 std::mem::needs_drop::<T>()
67 }
68
69 fn clone_boxed(&self) -> Box<dyn RecordValue> {
70 Box::new(self.clone())
71 }
72
73 fn assign_into(&self, target: &mut Option<Box<dyn RecordValue>>) {
74 match target
75 .as_mut()
76 .and_then(|value| (&mut **value as &mut dyn Any).downcast_mut::<T>())
77 {
78 Some(slot) => slot.clone_from(self),
79 None => *target = Some(Box::new(self.clone())),
80 }
81 }
82}
83
84#[derive(Debug, Clone, Copy, Eq, PartialEq)]
85pub(crate) enum StateRecordValueError {
86 Missing,
87}
88
89impl StateRecord {
90 pub(crate) fn new<T: Any + Clone>(snapshot_id: SnapshotId, value: T) -> Rc<Self> {
91 Self::with_boxed(snapshot_id, Box::new(value))
92 }
93
94 fn with_boxed(snapshot_id: SnapshotId, value: Box<dyn RecordValue>) -> Rc<Self> {
95 Rc::new(Self {
96 snapshot_id: Cell::new(snapshot_id),
97 tombstone: Cell::new(false),
98 value: RefCell::new(Some(value)),
99 })
100 }
101
102 #[inline]
103 pub(crate) fn snapshot_id(&self) -> SnapshotId {
104 self.snapshot_id.get()
105 }
106
107 #[inline]
108 pub(crate) fn set_snapshot_id(&self, id: SnapshotId) {
109 self.snapshot_id.set(id);
110 }
111
112 #[inline]
113 pub(crate) fn is_tombstone(&self) -> bool {
114 self.tombstone.get()
115 }
116
117 #[inline]
118 pub(crate) fn set_tombstone(&self, tombstone: bool) {
119 self.tombstone.set(tombstone);
120 }
121
122 #[must_use = "drop the cleared value once the record chain is consistent"]
128 pub(crate) fn clear_value(&self) -> Option<Box<dyn RecordValue>> {
129 let mut value = self.value.borrow_mut();
130 if value
131 .as_ref()
132 .is_some_and(|value| (**value).holds_resources())
133 {
134 value.take()
135 } else {
136 None
137 }
138 }
139
140 pub(crate) fn tombstone_retiring_value(&self) {
143 self.set_tombstone(true);
144 if let Some(value) = self.clear_value() {
145 retire_record_value(value);
146 }
147 }
148
149 #[must_use = "drop the displaced value once the write is published"]
153 pub(crate) fn replace_value<T: Any + Clone>(&self, new_value: T) -> Option<T> {
154 let mut value = self.value.borrow_mut();
155 if let Some(slot) = value
156 .as_mut()
157 .and_then(|current| (&mut **current as &mut dyn Any).downcast_mut::<T>())
158 {
159 return Some(std::mem::replace(slot, new_value));
160 }
161 let placeholder = value.replace(Box::new(new_value));
162 drop(value);
163 drop(placeholder);
164 None
165 }
166
167 fn cloned_value(&self) -> Option<Box<dyn RecordValue>> {
168 self.value
169 .borrow()
170 .as_ref()
171 .map(|value| (**value).clone_boxed())
172 }
173
174 fn cloned_value_of(&self, expected: TypeId) -> Option<Box<dyn RecordValue>> {
175 self.value
176 .borrow()
177 .as_ref()
178 .filter(|value| (&***value as &dyn Any).type_id() == expected)
179 .map(|value| (**value).clone_boxed())
180 }
181
182 fn set_boxed_value(&self, value: Box<dyn RecordValue>) {
183 let displaced = self.value.borrow_mut().replace(value);
184 if let Some(displaced) = displaced {
185 retire_record_value(displaced);
186 }
187 }
188
189 pub(crate) fn with_value<T: Any, R>(&self, f: impl FnOnce(&T) -> R) -> R {
190 self.try_with_value(f)
191 .unwrap_or_else(|| panic!("StateRecord value missing or wrong type"))
192 }
193
194 pub(crate) fn try_with_value<T: Any, R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
195 let guard = self.value.borrow();
196 let value = guard
197 .as_ref()
198 .and_then(|boxed| (&**boxed as &dyn Any).downcast_ref::<T>())?;
199 Some(f(value))
200 }
201
202 #[cfg(test)]
203 pub(crate) fn clear_for_reuse(&self) {
204 drop(self.value.borrow_mut().take());
205 }
206
207 pub(crate) fn assign_value(&self, source: &Self) -> Result<(), StateRecordValueError> {
208 if std::ptr::eq(self, source) {
209 return match self.value.borrow().is_some() {
210 true => Ok(()),
211 false => Err(StateRecordValueError::Missing),
212 };
213 }
214 let source_value = source.value.borrow();
215 let value = source_value
216 .as_ref()
217 .ok_or(StateRecordValueError::Missing)?;
218 (**value).assign_into(&mut self.value.borrow_mut());
219 Ok(())
220 }
221}
222
223#[cold]
224fn log_merge_value_missing(which: &str, state: ObjectId, record: &StateRecord) {
225 log::error!(
226 "SnapshotMutableState::merge_records {which} record value missing or wrong type (state {state:?}, {which}_id={})",
227 record.snapshot_id()
228 );
229}
230
231fn install_copy_as_head(
232 state: &dyn StateObject,
233 source: &StateRecord,
234 expected: TypeId,
235) -> Option<SnapshotId> {
236 let value = source.cloned_value_of(expected)?;
237 let new_id = allocate_record_id();
238 let record = state.records().overwritable_head();
239 record.set_boxed_value(value);
240 record.set_tombstone(false);
241 record.set_snapshot_id(new_id);
242 advance_global_snapshot(new_id);
243 Some(new_id)
244}
245
246fn promote_record_locked(
247 state: &dyn StateObject,
248 child_id: SnapshotId,
249 expected: TypeId,
250) -> Result<(), &'static str> {
251 let Some(record) = state.records().at(child_id) else {
252 log::error!(
253 "SnapshotMutableState::promote_record missing child record (state {:?}, child_id={})",
254 state.object_id(),
255 child_id
256 );
257 return Err("missing child record");
258 };
259 if install_copy_as_head(state, &record, expected).is_none() {
260 log::error!(
261 "SnapshotMutableState::promote_record child record value missing or wrong type (state {:?}, child_id={})",
262 state.object_id(),
263 child_id
264 );
265 return Err("child record value missing or wrong type");
266 }
267 Ok(())
268}
269
270fn commit_merged_record_locked(
271 state: &dyn StateObject,
272 merged: &StateRecord,
273 expected: TypeId,
274) -> Result<SnapshotId, &'static str> {
275 install_copy_as_head(state, merged, expected).ok_or_else(|| {
276 log::error!(
277 "SnapshotMutableState::commit_merged_record merged record value missing or wrong type (state {:?}, merged_id={})",
278 state.object_id(),
279 merged.snapshot_id()
280 );
281 "merged record value missing or wrong type"
282 })
283}
284
285pub(crate) fn retire_record_value(value: Box<dyn RecordValue>) {
289 let held = crate::thread_state::try_snapshots(|state| {
290 if state.retire_depth.get() == 0 {
291 return Some(value);
292 }
293 state.retired_record_values.borrow_mut().push(value);
294 None
295 });
296 drop(held);
297}
298
299pub(crate) struct RetiredValuesScope(());
302
303impl RetiredValuesScope {
304 pub(crate) fn enter() -> Self {
305 crate::thread_state::snapshots(|state| {
306 state.retire_depth.set(state.retire_depth.get() + 1);
307 });
308 Self(())
309 }
310}
311
312impl Drop for RetiredValuesScope {
313 fn drop(&mut self) {
314 let retired = crate::thread_state::snapshots(|state| {
315 let depth = state.retire_depth.get() - 1;
316 state.retire_depth.set(depth);
317 (depth == 0).then(|| state.retired_record_values.take())
318 });
319 drop(retired);
320 }
321}
322
323fn active_snapshot() -> AnySnapshot {
324 current_snapshot().unwrap_or_else(|| AnySnapshot::Global(GlobalSnapshot::get_or_create()))
325}
326
327pub(crate) trait MutationPolicy<T>: Send + Sync {
328 fn equivalent(&self, a: &T, b: &T) -> bool;
329 fn merge(&self, _previous: &T, _current: &T, _applied: &T) -> Option<T> {
330 None
331 }
332}
333
334pub(crate) struct NeverEqual;
335
336impl<T> MutationPolicy<T> for NeverEqual {
337 fn equivalent(&self, _a: &T, _b: &T) -> bool {
338 false
339 }
340}
341
342pub(crate) struct StructuralEqual;
343
344impl<T: PartialEq> MutationPolicy<T> for StructuralEqual {
345 fn equivalent(&self, a: &T, b: &T) -> bool {
346 a == b
347 }
348}
349
350pub trait StateObject: Any {
351 fn object_id(&self) -> ObjectId;
352 fn records(&self) -> &RecordChain;
353 fn try_readable_record(
354 &self,
355 snapshot_id: SnapshotId,
356 invalid: &SnapshotIdSet,
357 ) -> Option<Rc<StateRecord>>;
358 fn readable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord>;
359
360 fn merge_records(
361 &self,
362 _previous: Rc<StateRecord>,
363 _current: Rc<StateRecord>,
364 _applied: Rc<StateRecord>,
365 ) -> Option<Rc<StateRecord>> {
366 None
367 }
368
369 fn commit_merged_record(&self, _merged: Rc<StateRecord>) -> Result<SnapshotId, &'static str> {
370 Err("StateObject does not support merged record commits")
371 }
372 fn promote_record(&self, child_id: SnapshotId) -> Result<(), &'static str>;
373
374 fn overwrite_unused_records(&self) -> bool {
375 false
376 }
377
378 fn observation_lease(&self) -> Option<Rc<dyn Any>> {
379 None
380 }
381
382 fn as_any(&self) -> &dyn Any;
383}
384
385pub(crate) struct SnapshotMutableState<T> {
386 records: RecordChain,
387 policy: Rc<dyn MutationPolicy<T>>,
388 id: ObjectId,
389 weak_self: Weak<Self>,
390 apply_observer: ApplyObserver,
391 subscribers: Subscribers,
392}
393
394#[derive(Default)]
399pub(crate) struct Subscribers {
400 lease: Rc<()>,
401 scopes: Cell<usize>,
402 callbacks: RefCell<Vec<Rc<dyn Fn()>>>,
403}
404
405impl Subscribers {
406 fn acquire_observation_lease(&self) -> Option<Rc<dyn Any>> {
407 let was_empty = !self.has_subscribers();
408 let lease = Rc::clone(&self.lease);
409 if was_empty {
410 notify_subscriber_callbacks(&self.callbacks);
411 }
412 Some(lease)
413 }
414
415 fn add_scope_observer(&self) -> bool {
416 let was_empty = !self.has_subscribers();
417 let count = self
418 .scopes
419 .get()
420 .checked_add(1)
421 .expect("state scope observation count overflow");
422 self.scopes.set(count);
423 was_empty
424 }
425
426 fn remove_scope_observers(&self, count: usize) {
427 if count == 0 {
428 return;
429 }
430 let remaining = self
431 .scopes
432 .get()
433 .checked_sub(count)
434 .expect("state scope observation count underflow");
435 self.scopes.set(remaining);
436 }
437
438 fn has_subscribers(&self) -> bool {
439 Rc::strong_count(&self.lease) > 1 || self.scopes.get() > 0
440 }
441
442 fn subscriber_callback(&self, callback: Rc<dyn Fn()>, notify: bool) {
443 self.callbacks.borrow_mut().push(Rc::clone(&callback));
444 if notify {
445 callback();
446 }
447 }
448
449 fn notify_subscribers(&self) {
450 notify_subscriber_callbacks(&self.callbacks);
451 }
452}
453
454#[inline(always)]
455fn assert_record_chain(
456 records: &RecordChain,
457 id: ObjectId,
458 caller: &str,
459 snapshot_context: Option<SnapshotId>,
460) {
461 if should_check_chain_integrity() {
462 records.check_integrity(id, caller, snapshot_context);
463 }
464}
465
466enum Merge<'a, T> {
467 KeepCurrent,
468 Value(Option<T>),
469 Missing(&'static str, &'a Rc<StateRecord>),
470}
471
472type ApplyObserver = OnceCell<Box<dyn Fn() + 'static>>;
473
474fn notify_applied(observer: &ApplyObserver) {
475 if let Some(observer) = observer.get() {
476 observer();
477 }
478}
479
480fn finish_apply(
481 observer: &ApplyObserver,
482 records: &RecordChain,
483 id: ObjectId,
484 caller: &str,
485 snapshot_id: SnapshotId,
486) {
487 notify_applied(observer);
488 assert_record_chain(records, id, caller, Some(snapshot_id));
489}
490
491struct WriteTarget<'a> {
494 records: &'a RecordChain,
495 id: ObjectId,
496}
497
498impl WriteTarget<'_> {
499 fn assert_chain_integrity(&self, caller: &str, snapshot_context: Option<SnapshotId>) {
500 assert_record_chain(self.records, self.id, caller, snapshot_context);
501 }
502
503 fn begin_global_write(&self, global: &GlobalSnapshot, snapshot_id: SnapshotId) {
504 assert!(
505 !global.has_pending_children(),
506 "SnapshotMutableState::set attempted global write while pending children {:?} exist (state {:?}, snapshot_id={})",
507 global.pending_children(),
508 self.id,
509 snapshot_id
510 );
511 mark_update_write(self.id);
512 }
513
514 fn push_global_record(&self) -> (Rc<StateRecord>, SnapshotId) {
517 let new_id = allocate_record_id();
518 (self.records.overwritable_head(), new_id)
519 }
520
521 fn publish_global_record(
524 &self,
525 record: &StateRecord,
526 new_id: SnapshotId,
527 snapshot_id: SnapshotId,
528 ) {
529 record.set_snapshot_id(new_id);
530 record.set_tombstone(false);
531 advance_global_snapshot(new_id);
532 self.assert_chain_integrity("set(global-push)", Some(snapshot_id));
533 }
534
535 fn finish_global_write(
538 &self,
539 record: &StateRecord,
540 written: Option<Rc<dyn StateObject>>,
541 snapshot_id: SnapshotId,
542 ) {
543 let mut cleared: SmallVec<[Box<dyn RecordValue>; 2]> = SmallVec::new();
544 self.records.clear_hidden_after(record, &mut cleared);
545 self.assert_chain_integrity("set(global-tombstone)", Some(snapshot_id));
546 if let Some(modified) = written.as_ref() {
547 crate::snapshot_v2::notify_apply_observers(
548 std::slice::from_ref(modified),
549 record.snapshot_id(),
550 );
551 }
552 drop(cleared);
553 }
554
555 fn begin_child_write(
556 &self,
557 snapshot: &AnySnapshot,
558 snapshot_id: SnapshotId,
559 invalid: &SnapshotIdSet,
560 written: Option<Rc<dyn StateObject>>,
561 ) -> Rc<StateRecord> {
562 if let Some(state) = written {
563 snapshot.record_write(state);
564 }
565 mark_update_write(self.id);
566 self.writable_record(snapshot_id, invalid)
567 }
568
569 fn writable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord> {
570 let Some(readable) = self.records.readable(snapshot_id, invalid) else {
571 let cloned_value = self
572 .records
573 .head()
574 .cloned_value()
575 .unwrap_or_else(|| panic!("StateRecord value missing or wrong type"));
576 let new_head = StateRecord::with_boxed(snapshot_id, cloned_value);
577 self.records.prepend(Rc::clone(&new_head));
578 self.assert_chain_integrity("writable_record(recover)", Some(snapshot_id));
579 return new_head;
580 };
581
582 if readable.snapshot_id() == snapshot_id {
583 return readable;
584 }
585
586 let overwritable = self.records.overwritable();
587 if let Err(error) = overwritable.assign_value(&readable) {
588 log::error!(
589 "snapshot writable record could not copy the readable value for state {:?}: {:?}",
590 self.id,
591 error
592 );
593 }
594 overwritable.set_snapshot_id(snapshot_id);
595 overwritable.set_tombstone(false);
596
597 self.assert_chain_integrity("writable_record(reuse)", Some(snapshot_id));
598
599 overwritable
600 }
601}
602
603trait PendingWrite {
606 fn matches(&self, record: &StateRecord) -> bool;
608 fn store(&mut self, record: &StateRecord);
611 fn written(&self) -> Option<Rc<dyn StateObject>>;
613}
614
615struct TypedWrite<'a, T> {
616 state: &'a SnapshotMutableState<T>,
617 value: Option<T>,
618 displaced: Option<T>,
619}
620
621impl<T: Clone + 'static> PendingWrite for TypedWrite<'_, T> {
622 fn matches(&self, record: &StateRecord) -> bool {
623 self.value.as_ref().is_some_and(|value| {
624 record.with_value(|current: &T| self.state.policy.equivalent(current, value))
625 })
626 }
627
628 fn store(&mut self, record: &StateRecord) {
629 if let Some(value) = self.value.take() {
630 self.displaced = record.replace_value(value);
631 }
632 }
633
634 fn written(&self) -> Option<Rc<dyn StateObject>> {
635 self.state.written_state()
636 }
637}
638
639#[inline(never)]
643fn write_value(target: &WriteTarget<'_>, write: &mut dyn PendingWrite) -> bool {
644 #[cfg(debug_assertions)]
645 {
646 let in_handler = crate::in_event_handler();
647 let in_snapshot = crate::in_applied_snapshot();
648 if in_handler && !in_snapshot {
649 log::warn!(
650 target: "cranpose::state",
651 "State modified in event handler without run_in_mutable_snapshot; \
652 this can make updates invisible to other contexts. Wrap the handler \
653 in run_in_mutable_snapshot() or dispatch_ui_event(). State: {:?}",
654 target.id
655 );
656 }
657 }
658
659 let snapshot = active_snapshot();
660 let snapshot_id = snapshot.snapshot_id();
661 let readable_for =
662 |snapshot_id, invalid: &SnapshotIdSet| target.records.readable(snapshot_id, invalid);
663
664 match &snapshot {
665 AnySnapshot::Global(global) => {
666 let readable = snapshot.with_invalid(|invalid| readable_for(snapshot_id, invalid));
667 if readable
668 .as_deref()
669 .is_some_and(|record| write.matches(record))
670 {
671 return false;
672 }
673 target.begin_global_write(global, snapshot_id);
674 if let Some(record) = readable.filter(|record| record.snapshot_id() == snapshot_id) {
679 write.store(&record);
680 target.finish_global_write(&record, write.written(), snapshot_id);
681 } else {
682 let (record, new_id) = target.push_global_record();
683 write.store(&record);
684 target.publish_global_record(&record, new_id, snapshot_id);
685 target.finish_global_write(&record, write.written(), snapshot_id);
686 }
687 }
688 AnySnapshot::Mutable(_)
689 | AnySnapshot::NestedMutable(_)
690 | AnySnapshot::TransparentMutable(_) => {
691 let invalid = snapshot.invalid();
692 if readable_for(snapshot_id, &invalid)
693 .as_deref()
694 .is_some_and(|record| write.matches(record))
695 {
696 return false;
697 }
698 let record =
699 target.begin_child_write(&snapshot, snapshot_id, &invalid, write.written());
700 write.store(&record);
701 target.assert_chain_integrity("set(child-writable)", Some(snapshot_id));
702 }
703 AnySnapshot::Readonly(_)
704 | AnySnapshot::NestedReadonly(_)
705 | AnySnapshot::TransparentReadonly(_) => {
706 panic!("Cannot write to a read-only snapshot");
707 }
708 }
709
710 true
711}
712
713fn should_check_chain_integrity() -> bool {
714 #[cfg(debug_assertions)]
715 {
716 true
717 }
718
719 #[cfg(not(debug_assertions))]
720 {
721 crate::DIAGNOSTICS.assert_state_chain.is_set()
722 }
723}
724
725impl<T: Clone + 'static> SnapshotMutableState<T> {
726 fn readable_record_for_active_snapshot(&self) -> Result<Rc<StateRecord>, StateReadFailure> {
727 readable_record_for_active_snapshot(ReadNotice::Observers(self), &self.records, self.id)
728 }
729
730 fn readable_for(
731 &self,
732 snapshot_id: SnapshotId,
733 invalid: &SnapshotIdSet,
734 ) -> Option<Rc<StateRecord>> {
735 self.records.readable(snapshot_id, invalid)
736 }
737
738 pub(crate) fn new_in_rc(initial: T, policy: Rc<dyn MutationPolicy<T>>) -> Rc<Self> {
739 let snapshot = active_snapshot();
740 let snapshot_id = snapshot.snapshot_id();
741
742 let tail = StateRecord::new(PREEXISTING_SNAPSHOT_ID, initial.clone());
743 let head = StateRecord::new(snapshot_id, initial);
744
745 Rc::new_cyclic(|weak_self| Self {
746 records: RecordChain::new(smallvec![head, tail]),
747 policy,
748 id: ObjectId(weak_self.as_ptr() as usize),
749 weak_self: Weak::clone(weak_self),
750 apply_observer: OnceCell::new(),
751 subscribers: Subscribers::default(),
752 })
753 }
754
755 pub(crate) fn set_apply_observer(&self, observer: Box<dyn Fn() + 'static>) {
756 assert!(
757 self.apply_observer.set(observer).is_ok(),
758 "state observer already installed"
759 );
760 }
761
762 fn finish_apply(&self, caller: &str, snapshot_id: SnapshotId) {
763 finish_apply(
764 &self.apply_observer,
765 &self.records,
766 self.id,
767 caller,
768 snapshot_id,
769 );
770 }
771
772 fn upgrade_self(&self) -> Option<Rc<Self>> {
773 self.weak_self.upgrade()
774 }
775
776 fn written_state(&self) -> Option<Rc<dyn StateObject>> {
777 self.upgrade_self()
778 .map(|state| -> Rc<dyn StateObject> { state })
779 }
780
781 #[inline]
782 pub(crate) fn id(&self) -> ObjectId {
783 self.id
784 }
785
786 pub(crate) fn try_with_value<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
787 let record = self.readable_record_for_active_snapshot().ok()?;
788 record.try_with_value(f)
789 }
790
791 pub(crate) fn try_get(&self) -> Option<T> {
792 self.try_with_value(Clone::clone)
793 }
794
795 pub(crate) fn with_value<R>(&self, f: impl FnOnce(&T) -> R) -> R {
796 let record = self
797 .readable_record_for_active_snapshot()
798 .unwrap_or_else(|failure| panic!("{failure}"));
799 record.with_value(f)
800 }
801
802 pub(crate) fn get(&self) -> T {
803 self.with_value(Clone::clone)
804 }
805
806 pub(crate) fn set(&self, new_value: T) -> bool {
807 let target = WriteTarget {
808 records: &self.records,
809 id: self.id,
810 };
811 let mut write = TypedWrite {
812 state: self,
813 value: Some(new_value),
814 displaced: None,
815 };
816 write_value(&target, &mut write)
819 }
820
821 fn try_readable_record(
822 &self,
823 snapshot_id: SnapshotId,
824 invalid: &SnapshotIdSet,
825 ) -> Option<Rc<StateRecord>> {
826 self.readable_for(snapshot_id, invalid)
827 }
828}
829
830pub(crate) struct UpdateScope {
831 id: ObjectId,
832 finished: bool,
833}
834
835impl UpdateScope {
836 pub(crate) fn new(id: ObjectId) -> Self {
837 crate::thread_state::snapshots(|state| {
838 state.active_updates.borrow_mut().insert(id);
839 state.pending_writes.borrow_mut().remove(&id);
840 });
841 Self {
842 id,
843 finished: false,
844 }
845 }
846
847 pub(crate) fn finish(mut self) -> bool {
848 self.finished = true;
849 crate::thread_state::snapshots(|state| {
850 state.active_updates.borrow_mut().remove(&self.id);
851 state.pending_writes.borrow_mut().remove(&self.id)
852 })
853 }
854}
855
856impl Drop for UpdateScope {
857 fn drop(&mut self) {
858 if self.finished {
859 return;
860 }
861 crate::thread_state::snapshots(|state| {
862 state.active_updates.borrow_mut().remove(&self.id);
863 state.pending_writes.borrow_mut().remove(&self.id);
864 });
865 }
866}
867
868fn mark_update_write(id: ObjectId) {
869 crate::thread_state::snapshots(|state| {
870 if state.active_updates.borrow().contains(&id) {
871 state.pending_writes.borrow_mut().insert(id);
872 }
873 });
874}
875
876impl<T: Clone + 'static> StateObject for SnapshotMutableState<T> {
877 fn object_id(&self) -> ObjectId {
878 self.id
879 }
880
881 fn records(&self) -> &RecordChain {
882 &self.records
883 }
884
885 fn try_readable_record(
886 &self,
887 snapshot_id: SnapshotId,
888 invalid: &SnapshotIdSet,
889 ) -> Option<Rc<StateRecord>> {
890 self.try_readable_record(snapshot_id, invalid)
891 }
892
893 fn readable_record(&self, snapshot_id: SnapshotId, invalid: &SnapshotIdSet) -> Rc<StateRecord> {
894 self.try_readable_record(snapshot_id, invalid)
895 .unwrap_or_else(|| {
896 panic!(
897 "SnapshotMutableState::readable_record returned null (state={:?}, snapshot_id={})",
898 self.id, snapshot_id
899 )
900 })
901 }
902
903 fn observation_lease(&self) -> Option<Rc<dyn Any>> {
904 self.subscribers.acquire_observation_lease()
905 }
906
907 fn merge_records(
908 &self,
909 previous: Rc<StateRecord>,
910 current: Rc<StateRecord>,
911 applied: Rc<StateRecord>,
912 ) -> Option<Rc<StateRecord>> {
913 let outcome = current
914 .try_with_value(|current_value: &T| {
915 applied
916 .try_with_value(|applied_value: &T| {
917 if self.policy.equivalent(current_value, applied_value) {
918 return Merge::KeepCurrent;
919 }
920 previous
921 .try_with_value(|previous_value: &T| {
922 Merge::Value(self.policy.merge(
923 previous_value,
924 current_value,
925 applied_value,
926 ))
927 })
928 .unwrap_or(Merge::Missing("previous", &previous))
929 })
930 .unwrap_or(Merge::Missing("applied", &applied))
931 })
932 .unwrap_or(Merge::Missing("current", ¤t));
933 match outcome {
934 Merge::KeepCurrent => Some(current),
935 Merge::Value(merged) => {
936 merged.map(|value| StateRecord::new(applied.snapshot_id(), value))
937 }
938 Merge::Missing(which, record) => {
939 log_merge_value_missing(which, self.id, record);
940 None
941 }
942 }
943 }
944
945 fn promote_record(&self, child_id: SnapshotId) -> Result<(), &'static str> {
946 promote_record_locked(self, child_id, TypeId::of::<T>())?;
947 self.finish_apply("promote_record", child_id);
948 Ok(())
949 }
950
951 fn commit_merged_record(&self, merged: Rc<StateRecord>) -> Result<SnapshotId, &'static str> {
952 let new_id = commit_merged_record_locked(self, &merged, TypeId::of::<T>())?;
953 self.finish_apply("commit_merged_record", new_id);
954 Ok(new_id)
955 }
956
957 fn overwrite_unused_records(&self) -> bool {
958 self.records.overwrite_unused()
959 }
960
961 fn as_any(&self) -> &dyn Any {
962 self
963 }
964}
965
966pub(crate) struct MutableStateInner<T: Clone + 'static> {
967 pub(crate) state: Rc<SnapshotMutableState<T>>,
968 watchers: StateWatchers,
969}
970
971struct StateWatchers {
975 scopes: RefCell<HashMap<ScopeId, Weak<RecomposeScopeInner>>>,
976 runtime: RuntimeHandle,
977 state_id: Cell<Option<StateId>>,
978}
979
980fn notify_subscriber_callbacks(callbacks: &RefCell<Vec<Rc<dyn Fn()>>>) {
981 let registered = callbacks.borrow().len();
982 for index in 0..registered {
983 let callback = callbacks.borrow().get(index).map(Rc::clone);
984 if let Some(callback) = callback {
985 callback();
986 }
987 }
988}
989
990fn shrink_watchers_if_sparse(watchers: &mut HashMap<ScopeId, Weak<RecomposeScopeInner>>) {
991 let len = watchers.len();
992 let capacity = watchers.capacity();
993 if capacity > len.saturating_mul(4).max(32) {
994 watchers.shrink_to_fit();
995 }
996}
997
998fn watcher_invalidation(runtime: RuntimeHandle, state_id: StateId) -> Box<dyn Fn()> {
1002 Box::new(move || {
1003 let ui_runtime = runtime.clone();
1004 runtime.enqueue_ui_task(Box::new(move || {
1005 ui_runtime.with_state_arena(|arena| arena.invalidate_watchers(state_id));
1006 }));
1007 })
1008}
1009
1010impl<T: Clone + 'static> MutableStateInner<T> {
1011 pub(crate) fn new_with_policy(
1012 value: T,
1013 runtime: RuntimeHandle,
1014 policy: Rc<dyn MutationPolicy<T>>,
1015 ) -> Self {
1016 Self {
1017 state: SnapshotMutableState::new_in_rc(value, policy),
1018 watchers: StateWatchers {
1019 scopes: RefCell::new(HashMap::default()),
1020 runtime,
1021 state_id: Cell::new(None),
1022 },
1023 }
1024 }
1025
1026 pub(crate) fn install_snapshot_observer(&self, state_id: StateId) {
1027 self.watchers.state_id.set(Some(state_id));
1028 self.state.set_apply_observer(watcher_invalidation(
1029 self.watchers.runtime.clone(),
1030 state_id,
1031 ));
1032 }
1033
1034 fn has_subscribers(&self) -> bool {
1035 self.watchers.has_subscribers(&self.state.subscribers)
1036 }
1037
1038 pub(crate) fn forget_scope(&self, scope_id: ScopeId) {
1041 self.watchers
1042 .forget_scope(&self.state.subscribers, scope_id);
1043 }
1044
1045 pub(crate) fn unregister_scope(&self, scope_id: ScopeId) {
1046 self.watchers
1047 .unregister_scope(&self.state.subscribers, scope_id);
1048 }
1049
1050 pub(crate) fn invalidate_watchers(&self) {
1051 self.watchers
1052 .invalidate(&self.state.subscribers, std::any::type_name::<T>());
1053 }
1054
1055 fn register_current_scope(&self) {
1057 register_current_state_scope(&self.watchers, &self.state.subscribers);
1058 }
1059}
1060
1061impl StateWatchers {
1062 fn register_scope(&self, subscribers: &Subscribers, scope: &RecomposeScope) -> (bool, bool) {
1072 let mut watchers = self.scopes.borrow_mut();
1073 let id = scope.id();
1074 match watchers.get(&id) {
1075 Some(existing) if existing.strong_count() > 0 => return (false, false),
1076 Some(_) => {
1077 watchers.remove(&id);
1078 subscribers.remove_scope_observers(1);
1079 }
1080 None => {}
1081 }
1082 let any_live = watchers.values().any(|watcher| watcher.strong_count() > 0);
1083 if !any_live || watchers.len() >= watchers.capacity() {
1084 let before = watchers.len();
1085 watchers.retain(|_, watcher| watcher.strong_count() > 0);
1086 subscribers.remove_scope_observers(before - watchers.len());
1087 }
1088 watchers.insert(id, scope.downgrade());
1089 drop(watchers);
1090 (true, subscribers.add_scope_observer())
1091 }
1092
1093 fn has_subscribers(&self, subscribers: &Subscribers) -> bool {
1094 let mut watchers = self.scopes.borrow_mut();
1095 let before = watchers.len();
1096 watchers.retain(|_, existing| existing.upgrade().is_some());
1097 subscribers.remove_scope_observers(before - watchers.len());
1098 subscribers.has_subscribers()
1099 }
1100
1101 fn forget_scope(&self, subscribers: &Subscribers, scope_id: ScopeId) {
1102 let mut watchers = self.scopes.borrow_mut();
1103 let removed = watchers.remove(&scope_id).is_some();
1104 if removed {
1105 shrink_watchers_if_sparse(&mut watchers);
1106 }
1107 drop(watchers);
1108 subscribers.remove_scope_observers(usize::from(removed));
1109 }
1110
1111 fn unregister_scope(&self, subscribers: &Subscribers, scope_id: ScopeId) {
1112 let mut watchers = self.scopes.borrow_mut();
1113 let removed = if watchers
1114 .get(&scope_id)
1115 .is_some_and(|weak| weak.upgrade().is_none())
1116 {
1117 watchers.remove(&scope_id);
1118 shrink_watchers_if_sparse(&mut watchers);
1119 true
1120 } else {
1121 false
1122 };
1123 drop(watchers);
1124 subscribers.remove_scope_observers(usize::from(removed));
1125 }
1126
1127 fn invalidate(&self, subscribers: &Subscribers, type_name: &'static str) {
1130 let (watchers, removed_count): (SmallVec<[RecomposeScope; 4]>, usize) = {
1132 let mut watchers = self.scopes.borrow_mut();
1133 let before = watchers.len();
1134 let mut live = SmallVec::new();
1135 watchers.retain(|_, scope| {
1136 if let Some(inner) = scope.upgrade() {
1137 live.push(RecomposeScope { inner });
1138 true
1139 } else {
1140 false
1141 }
1142 });
1143 let removed_count = before - watchers.len();
1144 shrink_watchers_if_sparse(&mut watchers);
1145 (live, removed_count)
1146 };
1147 subscribers.remove_scope_observers(removed_count);
1148
1149 for watcher in watchers {
1150 debug_record_scope_invalidation(watcher.id(), self.state_id.get(), type_name);
1151 if let Some(state_id) = self.state_id.get() {
1152 watcher.invalidate_from_state(state_id);
1153 } else {
1154 watcher.invalidate();
1155 }
1156 }
1157 }
1158}
1159
1160impl<T: Clone + 'static> Drop for MutableStateInner<T> {
1161 fn drop(&mut self) {
1162 self.state
1163 .subscribers
1164 .remove_scope_observers(self.watchers.scopes.get_mut().len());
1165 }
1166}
1167
1168#[inline(never)]
1171fn register_current_state_scope(watchers: &StateWatchers, subscribers: &Subscribers) {
1172 let state_id = watchers.state_id.get();
1173 let Some(Some((scope, read))) =
1174 with_current_composer_opt(|composer| composer.note_state_read(state_id))
1175 else {
1176 return;
1177 };
1178 let (registered, became_subscribed) = watchers.register_scope(subscribers, &scope);
1179 if let (crate::ScopeRead::NotReading, true, Some(state_id)) = (read, registered, state_id) {
1180 scope.record_state_subscription(state_id);
1181 }
1182 if became_subscribed {
1183 subscribers.notify_subscribers();
1184 }
1185}
1186
1187trait StateArenaHandle<T: Clone + 'static> {
1188 fn state_id(&self) -> StateId;
1189 fn runtime_id(&self) -> runtime::RuntimeId;
1190
1191 fn runtime_handle(&self) -> RuntimeHandle {
1192 runtime::runtime_handle_by_id(self.runtime_id())
1193 .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
1194 }
1195
1196 fn runtime_handle_opt(&self) -> Option<RuntimeHandle> {
1197 runtime::runtime_handle_by_id(self.runtime_id())
1198 }
1199
1200 fn with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1201 runtime::with_state_arena_by_id(self.runtime_id(), |arena| {
1202 arena.with_typed::<T, R>(self.state_id(), f)
1203 })
1204 .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id()))
1205 }
1206
1207 fn try_with_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
1208 runtime::with_state_arena_by_id(self.runtime_id(), |arena| {
1209 arena.with_typed_opt::<T, R>(self.state_id(), f)
1210 })?
1211 }
1212
1213 fn read_subscribed<R>(&self, read: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1214 self.with_inner(|inner| {
1215 let result = read(inner);
1216 inner.register_current_scope();
1217 result
1218 })
1219 }
1220}
1221
1222pub struct State<T: Clone + 'static> {
1224 id: StateId,
1225 runtime_id: runtime::RuntimeId,
1226 derived: bool,
1229 _marker: PhantomData<fn() -> T>,
1230}
1231
1232pub struct MutableState<T: Clone + 'static> {
1238 id: StateId,
1239 runtime_id: runtime::RuntimeId,
1240 _marker: PhantomData<fn() -> T>,
1241}
1242
1243#[derive(Clone)]
1245pub struct OwnedMutableState<T: Clone + 'static> {
1246 state: MutableState<T>,
1247 _lease: Rc<runtime::StateHandleLease>,
1248 _marker: PhantomData<fn() -> T>,
1249}
1250
1251impl<T: Clone + 'static> PartialEq for State<T> {
1252 fn eq(&self, other: &Self) -> bool {
1253 self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
1254 }
1255}
1256
1257impl<T: Clone + 'static> Eq for State<T> {}
1258
1259impl<T: Clone + 'static> PartialEq for MutableState<T> {
1260 fn eq(&self, other: &Self) -> bool {
1261 self.state_id() == other.state_id() && self.runtime_id() == other.runtime_id()
1262 }
1263}
1264
1265impl<T: Clone + 'static> PartialEq for OwnedMutableState<T> {
1268 fn eq(&self, other: &Self) -> bool {
1269 self.state == other.state
1270 }
1271}
1272
1273impl<T: Clone + 'static> Eq for MutableState<T> {}
1274
1275impl<T: Clone + 'static> Copy for State<T> {}
1276
1277impl<T: Clone + 'static> Clone for State<T> {
1278 fn clone(&self) -> Self {
1279 *self
1280 }
1281}
1282
1283impl<T: Clone + 'static> Copy for MutableState<T> {}
1284
1285impl<T: Clone + 'static> Clone for MutableState<T> {
1286 fn clone(&self) -> Self {
1287 *self
1288 }
1289}
1290
1291impl<T: Clone + 'static> StateArenaHandle<T> for State<T> {
1292 fn state_id(&self) -> StateId {
1293 self.id
1294 }
1295
1296 fn runtime_id(&self) -> runtime::RuntimeId {
1297 self.runtime_id
1298 }
1299}
1300
1301impl<T: Clone + 'static> State<T> {
1302 pub fn is_alive(&self) -> bool {
1305 self.try_with_inner(|_| ()).is_some()
1306 }
1307
1308 pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1311 self.try_with_fresh_inner(|inner| inner.state.try_with_value(f))?
1312 }
1313
1314 pub fn try_value(&self) -> Option<T> {
1317 self.try_with_fresh_inner(|inner| inner.state.try_get())?
1318 }
1319
1320 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1324 let value = self.read_fresh_subscribed(|inner| inner.state.get());
1325 f(&value)
1326 }
1327
1328 pub fn read<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1333 self.read_fresh_subscribed(|inner| inner.state.with_value(f))
1334 }
1335
1336 pub fn value(&self) -> T {
1339 self.read_fresh_subscribed(|inner| inner.state.get())
1340 }
1341
1342 pub fn get(&self) -> T {
1344 self.value()
1345 }
1346
1347 pub fn get_non_reactive(&self) -> T {
1351 self.with_fresh_inner(|inner| inner.state.value_unobserved())
1352 }
1353
1354 fn of_derived(self) -> Self {
1356 Self {
1357 derived: true,
1358 ..self
1359 }
1360 }
1361
1362 fn with_fresh_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1367 runtime::with_state_arena_by_id(self.runtime_id, |arena| {
1368 if self.derived {
1369 arena.refresh_derived(self.id);
1370 }
1371 arena.with_typed::<T, R>(self.id, f)
1372 })
1373 .unwrap_or_else(|| panic!("runtime {:?} dropped", self.runtime_id))
1374 }
1375
1376 fn try_with_fresh_inner<R>(&self, f: impl FnOnce(&MutableStateInner<T>) -> R) -> Option<R> {
1378 runtime::with_state_arena_by_id(self.runtime_id, |arena| {
1379 if self.derived {
1380 arena.refresh_derived(self.id);
1381 }
1382 arena.with_typed_opt::<T, R>(self.id, f)
1383 })?
1384 }
1385
1386 fn read_fresh_subscribed<R>(&self, read: impl FnOnce(&MutableStateInner<T>) -> R) -> R {
1389 self.with_fresh_inner(|inner| {
1390 let result = read(inner);
1391 inner.register_current_scope();
1392 result
1393 })
1394 }
1395
1396 pub fn has_subscribers(&self) -> bool {
1398 self.with_inner(MutableStateInner::has_subscribers)
1399 }
1400
1401 pub fn on_subscriber(&self, callback: impl Fn() + 'static) {
1407 let callback: Rc<dyn Fn()> = Rc::new(callback);
1408 self.with_inner(|inner| {
1409 inner
1410 .state
1411 .subscribers
1412 .subscriber_callback(callback, inner.has_subscribers());
1413 });
1414 }
1415
1416 pub fn subscription_hold(&self) -> StateSubscriptionHold {
1428 StateSubscriptionHold {
1429 _lease: self
1430 .try_with_inner(|inner| inner.state.observation_lease())
1431 .flatten(),
1432 }
1433 }
1434}
1435
1436pub struct StateSubscriptionHold {
1439 _lease: Option<Rc<dyn Any>>,
1440}
1441
1442impl<T: Clone + 'static> StateArenaHandle<T> for MutableState<T> {
1443 fn state_id(&self) -> StateId {
1444 self.id
1445 }
1446
1447 fn runtime_id(&self) -> runtime::RuntimeId {
1448 self.runtime_id
1449 }
1450}
1451
1452#[cold]
1456#[inline(never)]
1457fn log_skipped_replace(runtime_id: runtime::RuntimeId, state_id: StateId, runtime_alive: bool) {
1458 if runtime_alive {
1459 log::debug!(
1460 "MutableState::replace skipped: state cell released (slot={}, gen={})",
1461 state_id.slot(),
1462 state_id.generation(),
1463 );
1464 } else {
1465 log::debug!("MutableState::replace skipped: runtime {runtime_id:?} dropped");
1466 }
1467}
1468
1469impl<T: Clone + 'static> MutableState<T> {
1470 pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
1473 runtime.alloc_persistent_state(value)
1474 }
1475
1476 fn from_parts(id: StateId, runtime_id: runtime::RuntimeId) -> Self {
1477 Self {
1478 id,
1479 runtime_id,
1480 _marker: PhantomData,
1481 }
1482 }
1483
1484 pub(crate) fn from_lease(lease: &Rc<runtime::StateHandleLease>) -> Self {
1485 Self::from_parts(lease.id(), lease.runtime_id())
1486 }
1487
1488 pub fn is_alive(&self) -> bool {
1491 self.try_with_inner(|_| ()).is_some()
1492 }
1493
1494 pub fn try_with<R>(&self, f: impl FnOnce(&T) -> R) -> Option<R> {
1497 self.try_with_inner(|inner| inner.state.try_with_value(f))?
1498 }
1499
1500 pub fn try_value(&self) -> Option<T> {
1503 self.try_with_inner(|inner| inner.state.try_get())?
1504 }
1505
1506 pub fn as_state(&self) -> State<T> {
1508 State {
1509 id: self.id,
1510 runtime_id: self.runtime_id,
1511 derived: false,
1512 _marker: PhantomData,
1513 }
1514 }
1515
1516 pub fn try_retain(&self) -> Option<OwnedMutableState<T>> {
1519 let lease = self
1520 .runtime_handle_opt()?
1521 .retain_state_lease(self.state_id())?;
1522 Some(OwnedMutableState {
1523 state: *self,
1524 _lease: lease,
1525 _marker: PhantomData,
1526 })
1527 }
1528
1529 pub fn retain(&self) -> OwnedMutableState<T> {
1532 self.try_retain()
1533 .unwrap_or_else(|| panic!("state {:?} is no longer alive", self.state_id()))
1534 }
1535
1536 pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1540 let value = self.read_subscribed(|inner| inner.state.get());
1541 f(&value)
1542 }
1543
1544 pub fn read<R>(&self, f: impl FnOnce(&T) -> R) -> R {
1549 self.read_subscribed(|inner| inner.state.with_value(f))
1550 }
1551
1552 pub fn update<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
1555 let runtime = self.runtime_handle();
1556 runtime.assert_ui_thread();
1557 runtime.with_state_arena(|arena| {
1558 arena.with_typed::<T, R>(self.state_id(), |inner| {
1559 let mut value = inner.state.get();
1560 let tracker = UpdateScope::new(inner.state.id());
1561 let result = f(&mut value);
1562 let wrote_elsewhere = tracker.finish();
1563 if !wrote_elsewhere && inner.state.set(value) {
1564 inner.invalidate_watchers();
1565 }
1566 result
1567 })
1568 })
1569 }
1570
1571 pub fn replace(&self, value: T) {
1574 let replaced = runtime::with_state_arena_by_id(self.runtime_id(), |arena| {
1577 arena.with_typed_opt::<T, ()>(self.state_id(), |inner| {
1578 if inner.state.set(value) {
1579 inner.invalidate_watchers();
1580 }
1581 })
1582 });
1583 if replaced != Some(Some(())) {
1584 log_skipped_replace(self.runtime_id(), self.state_id(), replaced.is_some());
1585 }
1586 }
1587
1588 pub fn set_value(&self, value: T) {
1590 self.replace(value);
1591 }
1592
1593 pub fn set(&self, value: T) {
1595 self.replace(value);
1596 }
1597
1598 pub fn value(&self) -> T {
1601 self.read_subscribed(|inner| inner.state.get())
1602 }
1603
1604 pub fn get(&self) -> T {
1606 self.value()
1607 }
1608
1609 pub fn get_non_reactive(&self) -> T {
1611 self.with_inner(|inner| inner.state.value_unobserved())
1612 }
1613
1614 #[doc(hidden)]
1615 pub fn runtime_state_id(&self) -> StateId {
1616 self.state_id()
1617 }
1618
1619 #[doc(hidden)]
1620 pub fn subscribe_current_scope_only(&self) {
1621 self.subscribe_current_scope();
1622 }
1623
1624 fn subscribe_current_scope(&self) {
1625 self.with_inner(MutableStateInner::register_current_scope);
1626 }
1627
1628 #[cfg(test)]
1629 pub(crate) fn watcher_count(&self) -> usize {
1630 self.with_inner(|inner| inner.watchers.scopes.borrow().len())
1631 }
1632
1633 #[cfg(test)]
1634 pub(crate) fn watcher_capacity(&self) -> usize {
1635 self.with_inner(|inner| inner.watchers.scopes.borrow().capacity())
1636 }
1637
1638 #[cfg(test)]
1639 pub(crate) fn state_id_for_test(&self) -> StateId {
1640 self.state_id()
1641 }
1642
1643 #[cfg(test)]
1644 pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
1645 self.as_state().subscribe_scope_for_test(scope);
1646 }
1647}
1648
1649impl<T: Clone + 'static> OwnedMutableState<T> {
1650 pub fn with_runtime(value: T, runtime: RuntimeHandle) -> Self {
1653 let lease = runtime.alloc_state(value);
1654 Self {
1655 state: MutableState::from_lease(&lease),
1656 _lease: lease,
1657 _marker: PhantomData,
1658 }
1659 }
1660
1661 pub fn with_runtime_structural_eq(value: T, runtime: RuntimeHandle) -> Self
1664 where
1665 T: PartialEq,
1666 {
1667 Self::with_runtime_and_policy(value, runtime, Rc::new(StructuralEqual))
1668 }
1669
1670 pub(crate) fn with_runtime_and_policy(
1671 value: T,
1672 runtime: RuntimeHandle,
1673 policy: Rc<dyn MutationPolicy<T>>,
1674 ) -> Self {
1675 let lease = runtime.alloc_state_with_policy(value, policy);
1676 Self {
1677 state: MutableState::from_lease(&lease),
1678 _lease: lease,
1679 _marker: PhantomData,
1680 }
1681 }
1682
1683 pub fn handle(&self) -> MutableState<T> {
1685 self.state
1686 }
1687
1688 pub fn as_state(&self) -> State<T> {
1690 self.state.as_state()
1691 }
1692}
1693
1694impl<T: Clone + 'static> Deref for OwnedMutableState<T> {
1695 type Target = MutableState<T>;
1696
1697 fn deref(&self) -> &Self::Target {
1698 &self.state
1699 }
1700}
1701
1702#[cfg(test)]
1703impl<T: Clone + 'static> State<T> {
1704 pub(crate) fn subscribe_scope_for_test(&self, scope: &RecomposeScope) {
1705 self.with_inner(|inner| {
1706 let (registered, became_subscribed) = inner
1707 .watchers
1708 .register_scope(&inner.state.subscribers, scope);
1709 if registered {
1710 if let Some(state_id) = inner.watchers.state_id.get() {
1711 scope.record_state_subscription(state_id);
1712 }
1713 if became_subscribed {
1714 inner.state.subscribers.notify_subscribers();
1715 }
1716 }
1717 });
1718 }
1719}
1720
1721impl<T: fmt::Debug + Clone + 'static> fmt::Debug for MutableState<T> {
1722 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1723 if let Some(value) = self.try_value() {
1724 f.debug_struct("MutableState")
1725 .field("value", &value)
1726 .finish()
1727 } else {
1728 f.write_str("MutableState { value: <unavailable> }")
1729 }
1730 }
1731}
1732
1733#[derive(Clone)]
1736pub struct SnapshotStateMap<K, V>
1737where
1738 K: Clone + Eq + Hash + 'static,
1739 V: Clone + 'static,
1740{
1741 state: OwnedMutableState<HashMap<K, V>>,
1742}
1743
1744impl<K, V> SnapshotStateMap<K, V>
1745where
1746 K: Clone + Eq + Hash + 'static,
1747 V: Clone + 'static,
1748{
1749 pub fn with_runtime<I>(pairs: I, runtime: RuntimeHandle) -> Self
1751 where
1752 I: IntoIterator<Item = (K, V)>,
1753 {
1754 let map: HashMap<K, V> = pairs.into_iter().collect();
1755 Self {
1756 state: OwnedMutableState::with_runtime(map, runtime),
1757 }
1758 }
1759
1760 pub fn as_state(&self) -> State<HashMap<K, V>> {
1762 self.state.as_state()
1763 }
1764
1765 pub fn as_mutable_state(&self) -> MutableState<HashMap<K, V>> {
1767 self.state.handle()
1768 }
1769
1770 pub fn len(&self) -> usize {
1772 self.state.with(HashMap::len)
1773 }
1774
1775 pub fn is_empty(&self) -> bool {
1777 self.state.with(HashMap::is_empty)
1778 }
1779
1780 pub fn contains_key(&self, key: &K) -> bool {
1782 self.state.with(|map| map.contains_key(key))
1783 }
1784
1785 pub fn get(&self, key: &K) -> Option<V> {
1787 self.state.with(|map| map.get(key).cloned())
1788 }
1789
1790 pub fn to_hash_map(&self) -> HashMap<K, V> {
1792 self.state.with(Clone::clone)
1793 }
1794
1795 pub fn insert(&self, key: K, value: V) -> Option<V> {
1797 self.state.update(|map| map.insert(key, value))
1798 }
1799
1800 pub fn extend<I>(&self, iter: I)
1802 where
1803 I: IntoIterator<Item = (K, V)>,
1804 {
1805 self.state.update(|map| map.extend(iter));
1806 }
1807
1808 pub fn remove(&self, key: &K) -> Option<V> {
1810 self.state.update(|map| map.remove(key))
1811 }
1812
1813 pub fn clear(&self) {
1815 self.state.replace(HashMap::default());
1816 }
1817
1818 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
1841impl<T: fmt::Debug + Clone + 'static> fmt::Debug for State<T> {
1842 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1843 if let Some(value) = self.try_value() {
1844 f.debug_struct("State").field("value", &value).finish()
1845 } else {
1846 f.write_str("State { value: <unavailable> }")
1847 }
1848 }
1849}
1850
1851#[cfg(test)]
1852#[path = "tests/state_tests.rs"]
1853mod tests;