1#![allow(clippy::type_complexity)]
3
4use std::{
5 any::{Any, TypeId},
6 cell::{Cell, RefCell},
7 hash::{Hash, Hasher},
8 rc::{Rc, Weak},
9 sync::Arc,
10};
11
12use smallvec::SmallVec;
13
14use crate::{
15 collections::map::{HashMap, HashSet},
16 hash::default as default_hash,
17 snapshot_v2::{register_apply_observer, ReadObserver, StateObjectId},
18 state::StateObject,
19 RecomposeScope, RecomposeScopeInner, ScopeId,
20};
21
22type Executor = dyn Fn(Box<dyn FnOnce() + 'static>) + 'static;
24
25#[derive(Clone)]
38pub struct SnapshotStateObserver {
39 inner: Rc<SnapshotStateObserverInner>,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
43pub struct SnapshotStateObserverDebugStats {
44 pub scopes_len: usize,
45 pub scopes_cap: usize,
46 pub fast_scopes_len: usize,
47 pub fast_scopes_cap: usize,
48 pub stateless_scope_count: usize,
49 pub observed_state_count: usize,
50 pub observed_state_capacity: usize,
51}
52
53impl SnapshotStateObserver {
54 pub fn new(on_changed_executor: impl Fn(Box<dyn FnOnce() + 'static>) + 'static) -> Self {
56 let inner = Rc::new(SnapshotStateObserverInner::new(on_changed_executor));
57 inner.set_self(Rc::downgrade(&inner));
58 Self { inner }
59 }
60
61 pub fn observe_reads<T, R>(
67 &self,
68 scope: T,
69 on_value_changed_for_scope: impl Fn(&T) + 'static,
70 block: impl FnOnce() -> R,
71 ) -> R
72 where
73 T: Any + Clone + Eq + Hash + 'static,
74 {
75 self.inner
76 .observe_reads(scope, on_value_changed_for_scope, block)
77 }
78
79 pub fn begin_frame(&self) {
81 self.inner.begin_frame();
82 }
83
84 pub fn prune_dead_scopes(&self) {
86 self.inner.prune_dead_scopes();
87 }
88
89 pub fn with_no_observations<R>(&self, block: impl FnOnce() -> R) -> R {
91 self.inner.with_no_observations(block)
92 }
93
94 pub fn clear<T>(&self, scope: &T)
96 where
97 T: Any + Eq + Hash + 'static,
98 {
99 self.inner.clear(scope);
100 }
101
102 pub fn clear_if(&self, predicate: impl Fn(&dyn Any) -> bool) {
104 self.inner.clear_if(predicate);
105 }
106
107 pub fn clear_all(&self) {
109 self.inner.clear_all();
110 }
111
112 pub fn start(&self) {
114 let weak = Rc::downgrade(&self.inner);
115 self.inner.start(weak);
116 }
117
118 pub fn stop(&self) {
120 self.inner.stop();
121 }
122
123 pub fn debug_stats(&self) -> SnapshotStateObserverDebugStats {
124 self.inner.debug_stats()
125 }
126
127 #[cfg(test)]
129 pub fn notify_changes(&self, modified: &[Arc<dyn StateObject>]) {
130 self.inner.handle_apply(modified);
131 }
132}
133
134struct SnapshotStateObserverInner {
135 executor: Rc<Executor>,
149 owned_scopes: RefCell<HashMap<OwnedScopeIndexKey, OwnedScopeBucket>>,
150 fast_scopes: RefCell<HashMap<ScopeId, Rc<RefCell<ScopeEntry>>>>,
151 indexed_scopes: RefCell<HashMap<usize, Rc<RefCell<ScopeEntry>>>>,
152 observed_to_scopes: RefCell<HashMap<StateObjectId, HashSet<usize>>>,
153 pause_count: Rc<Cell<usize>>,
154 active_read_targets: Rc<RefCell<Vec<Rc<RefCell<ObservedIds>>>>>,
155 read_dispatcher: ReadObserver,
156 apply_handle: RefCell<Option<crate::snapshot_v2::ObserverHandle>>,
157 weak_self: RefCell<Weak<SnapshotStateObserverInner>>,
158 frame_version: Cell<u64>,
159 next_entry_id: Cell<usize>,
160}
161
162#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
163struct OwnedScopeIndexKey {
164 type_id: TypeId,
165 value_hash: u64,
166}
167
168type OwnedScopeBucket = SmallVec<[Rc<RefCell<ScopeEntry>>; 1]>;
169
170fn owned_scope_index_key<T>(scope: &T) -> OwnedScopeIndexKey
171where
172 T: Any + Hash + 'static,
173{
174 let mut hasher = default_hash::new();
175 scope.hash(&mut hasher);
176 OwnedScopeIndexKey {
177 type_id: TypeId::of::<T>(),
178 value_hash: hasher.finish(),
179 }
180}
181
182impl SnapshotStateObserverInner {
183 const MIN_RETAINED_SCOPE_CAPACITY: usize = 256;
184
185 fn new(on_changed_executor: impl Fn(Box<dyn FnOnce() + 'static>) + 'static) -> Self {
186 let pause_count = Rc::new(Cell::new(0));
187 let active_read_targets = Rc::new(RefCell::new(Vec::<Rc<RefCell<ObservedIds>>>::new()));
188 let dispatcher_pause_count = Rc::clone(&pause_count);
189 let dispatcher_targets = Rc::clone(&active_read_targets);
190 let read_dispatcher: ReadObserver = Arc::new(move |state| {
191 if dispatcher_pause_count.get() > 0 {
192 return;
193 }
194 let observed = dispatcher_targets.borrow().last().cloned();
195 if let Some(observed) = observed {
196 observed.borrow_mut().insert(state);
197 }
198 });
199
200 Self {
201 executor: Rc::new(on_changed_executor),
202 owned_scopes: RefCell::new(HashMap::default()),
203 fast_scopes: RefCell::new(HashMap::default()),
204 indexed_scopes: RefCell::new(HashMap::default()),
205 observed_to_scopes: RefCell::new(HashMap::default()),
206 pause_count,
207 active_read_targets,
208 read_dispatcher,
209 apply_handle: RefCell::new(None),
210 weak_self: RefCell::new(Weak::new()),
211 frame_version: Cell::new(0),
212 next_entry_id: Cell::new(0),
213 }
214 }
215
216 fn set_self(&self, weak: Weak<SnapshotStateObserverInner>) {
217 self.weak_self.replace(weak);
218 }
219
220 fn begin_frame(&self) {
221 let next = self.frame_version.get().wrapping_add(1);
222 self.frame_version.set(next);
223 self.prune_dead_scopes();
224 }
225
226 fn observe_reads<T, R>(
227 &self,
228 scope: T,
229 on_value_changed_for_scope: impl Fn(&T) + 'static,
230 block: impl FnOnce() -> R,
231 ) -> R
232 where
233 T: Any + Clone + Eq + Hash + 'static,
234 {
235 let frame_version = self.frame_version.get();
236 let has_frame_version = frame_version != 0;
237
238 let on_changed: Rc<dyn Fn(&dyn Any)> = {
239 let callback = Rc::new(on_value_changed_for_scope);
240 Rc::new(move |scope_any: &dyn Any| {
241 if let Some(typed) = scope_any.downcast_ref::<T>() {
242 callback(typed);
243 }
244 })
245 };
246
247 let existing_entry = self.find_scope_entry(&scope);
248 if let Some(entry) = existing_entry.as_ref() {
249 let already_observed = {
250 let mut entry_mut = entry.borrow_mut();
251 entry_mut.update(scope.clone(), on_changed.clone());
252 has_frame_version && entry_mut.last_seen_version == frame_version
253 };
254 if already_observed {
255 return block();
256 }
257 }
258
259 let observed = Rc::new(RefCell::new(ObservedIds::new()));
260 self.active_read_targets
261 .borrow_mut()
262 .push(Rc::clone(&observed));
263 struct ActiveObservationGuard {
264 stack: Rc<RefCell<Vec<Rc<RefCell<ObservedIds>>>>>,
265 }
266 impl Drop for ActiveObservationGuard {
267 fn drop(&mut self) {
268 self.stack.borrow_mut().pop();
269 }
270 }
271 let _guard = ActiveObservationGuard {
272 stack: Rc::clone(&self.active_read_targets),
273 };
274
275 let result = self.run_with_read_observer(block);
276
277 if observed.borrow().is_empty() {
278 if existing_entry.is_some() {
279 self.clear(&scope);
280 }
281 return result;
282 }
283
284 let observed = {
285 let mut observed = observed.borrow_mut();
286 std::mem::replace(&mut *observed, ObservedIds::new())
287 };
288 let entry = existing_entry
289 .unwrap_or_else(|| self.insert_scope_entry(scope.clone(), on_changed.clone()));
290 {
291 let mut entry_mut = entry.borrow_mut();
292 entry_mut.update(scope, on_changed);
293 entry_mut.last_seen_version = if has_frame_version {
294 frame_version
295 } else {
296 u64::MAX
297 };
298 }
299 self.replace_observed_ids(&entry, observed);
300
301 result
302 }
303
304 fn with_no_observations<R>(&self, block: impl FnOnce() -> R) -> R {
305 self.pause_count.set(self.pause_count.get() + 1);
306 let result = block();
307 self.pause_count
308 .set(self.pause_count.get().saturating_sub(1));
309 result
310 }
311
312 fn clear<T>(&self, scope: &T)
313 where
314 T: Any + Eq + Hash + 'static,
315 {
316 if let Some(rc_scope) = (scope as &dyn Any).downcast_ref::<RecomposeScope>() {
317 if let Some(entry) = self.fast_scopes.borrow_mut().remove(&rc_scope.id()) {
318 self.unregister_entry(&entry);
319 }
320 return;
321 }
322
323 let removed = self.remove_owned_scope_entry(scope);
324 if let Some(entry) = removed {
325 self.unregister_entry(&entry);
326 }
327 }
328
329 fn clear_if(&self, predicate: impl Fn(&dyn Any) -> bool) {
330 let removed_fast = {
331 let mut fast_scopes = self.fast_scopes.borrow_mut();
332 let removed_ids: Vec<_> = fast_scopes
333 .iter()
334 .filter(|(_, entry)| entry.borrow().matches_predicate(&predicate))
335 .map(|(scope_id, _)| *scope_id)
336 .collect();
337 removed_ids
338 .into_iter()
339 .filter_map(|scope_id| fast_scopes.remove(&scope_id))
340 .collect::<Vec<_>>()
341 };
342 let removed_owned =
343 { self.partition_owned_scopes(|entry| entry.matches_predicate(&predicate)) };
344
345 for entry in removed_fast.into_iter().chain(removed_owned) {
346 self.unregister_entry(&entry);
347 }
348 }
349
350 fn clear_all(&self) {
351 self.fast_scopes.borrow_mut().clear();
352 self.owned_scopes.borrow_mut().clear();
353 self.indexed_scopes.borrow_mut().clear();
354 self.observed_to_scopes.borrow_mut().clear();
355 }
356
357 fn start(&self, weak_self: Weak<SnapshotStateObserverInner>) {
358 if self.apply_handle.borrow().is_some() {
359 return;
360 }
361
362 let handle = register_apply_observer(Rc::new(move |modified, _snapshot_id| {
363 if let Some(inner) = weak_self.upgrade() {
364 inner.handle_apply(modified);
365 }
366 }));
367 self.apply_handle.replace(Some(handle));
368 }
369
370 fn stop(&self) {
371 if let Some(handle) = self.apply_handle.borrow_mut().take() {
372 drop(handle);
373 }
374 }
375
376 fn find_scope_entry<T>(&self, scope: &T) -> Option<Rc<RefCell<ScopeEntry>>>
377 where
378 T: Any + Eq + Hash + 'static,
379 {
380 if let Some(scope) = (scope as &dyn Any).downcast_ref::<RecomposeScope>() {
381 return self.fast_scopes.borrow().get(&scope.id()).cloned();
382 }
383
384 self.find_owned_scope_entry(scope)
385 }
386
387 fn insert_scope_entry(
388 &self,
389 scope: impl Any + Clone + Eq + Hash + 'static,
390 on_changed: Rc<dyn Fn(&dyn Any)>,
391 ) -> Rc<RefCell<ScopeEntry>> {
392 let entry_id = self.next_entry_id.get();
393 self.next_entry_id.set(entry_id.wrapping_add(1));
394 let recompose_scope_id = (&scope as &dyn Any)
395 .downcast_ref::<RecomposeScope>()
396 .map(RecomposeScope::id);
397 let owned_scope_key = recompose_scope_id
398 .is_none()
399 .then(|| owned_scope_index_key(&scope));
400 let entry = Rc::new(RefCell::new(ScopeEntry::new(entry_id, scope, on_changed)));
401 self.indexed_scopes
402 .borrow_mut()
403 .insert(entry_id, Rc::clone(&entry));
404 if let Some(scope_id) = recompose_scope_id {
405 self.fast_scopes
406 .borrow_mut()
407 .insert(scope_id, Rc::clone(&entry));
408 } else if let Some(scope_key) = owned_scope_key {
409 self.owned_scopes
410 .borrow_mut()
411 .entry(scope_key)
412 .or_default()
413 .push(Rc::clone(&entry));
414 }
415 entry
416 }
417
418 fn prune_dead_scopes(&self) {
419 let removed_fast = {
420 let mut fast_scopes = self.fast_scopes.borrow_mut();
421 let removed_ids: Vec<_> = fast_scopes
422 .iter()
423 .filter(|(_, entry)| !entry.borrow().should_retain())
424 .map(|(scope_id, _)| *scope_id)
425 .collect();
426 let removed = removed_ids
427 .into_iter()
428 .filter_map(|scope_id| fast_scopes.remove(&scope_id))
429 .collect::<Vec<_>>();
430 shrink_map_if_sparse(&mut fast_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
431 removed
432 };
433
434 let removed_owned = { self.partition_owned_scopes(|entry| !entry.should_retain()) };
435
436 for entry in removed_fast.into_iter().chain(removed_owned) {
437 self.unregister_entry(&entry);
438 }
439 }
440
441 fn find_owned_scope_entry<T>(&self, scope: &T) -> Option<Rc<RefCell<ScopeEntry>>>
442 where
443 T: Any + Eq + Hash + 'static,
444 {
445 let key = owned_scope_index_key(scope);
446 self.owned_scopes.borrow().get(&key).and_then(|bucket| {
447 bucket
448 .iter()
449 .find(|entry| entry.borrow().matches_scope(scope))
450 .cloned()
451 })
452 }
453
454 fn remove_owned_scope_entry<T>(&self, scope: &T) -> Option<Rc<RefCell<ScopeEntry>>>
455 where
456 T: Any + Eq + Hash + 'static,
457 {
458 let key = owned_scope_index_key(scope);
459 let mut owned_scopes = self.owned_scopes.borrow_mut();
460 let mut removed = None;
461 let mut remove_bucket = false;
462 if let Some(bucket) = owned_scopes.get_mut(&key) {
463 if let Some(index) = bucket
464 .iter()
465 .position(|entry| entry.borrow().matches_scope(scope))
466 {
467 removed = Some(bucket.remove(index));
468 remove_bucket = bucket.is_empty();
469 }
470 }
471 if remove_bucket {
472 owned_scopes.remove(&key);
473 }
474 shrink_map_if_sparse(&mut owned_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
475 removed
476 }
477
478 fn partition_owned_scopes(
479 &self,
480 should_remove: impl Fn(&ScopeEntry) -> bool,
481 ) -> Vec<Rc<RefCell<ScopeEntry>>> {
482 let mut owned_scopes = self.owned_scopes.borrow_mut();
483 let mut retained = HashMap::default();
484 let mut removed = Vec::new();
485 for (key, mut bucket) in owned_scopes.drain() {
486 let mut retained_bucket = OwnedScopeBucket::new();
487 for entry in bucket.drain(..) {
488 if should_remove(&entry.borrow()) {
489 removed.push(entry);
490 } else {
491 retained_bucket.push(entry);
492 }
493 }
494 if !retained_bucket.is_empty() {
495 retained.insert(key, retained_bucket);
496 }
497 }
498 *owned_scopes = retained;
499 shrink_map_if_sparse(&mut owned_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
500 removed
501 }
502
503 fn debug_stats(&self) -> SnapshotStateObserverDebugStats {
504 let owned_scopes = self.owned_scopes.borrow();
505 let fast_scopes = self.fast_scopes.borrow();
506 let owned_scope_len = owned_scopes.values().map(SmallVec::len).sum::<usize>();
507 let owned_scope_cap =
508 owned_scopes.capacity() + owned_scopes.values().map(SmallVec::capacity).sum::<usize>();
509 let scopes_len = owned_scope_len + fast_scopes.len();
510 let scopes_cap = owned_scope_cap + fast_scopes.capacity();
511 let mut observed_state_count = 0;
512 let mut observed_state_capacity = 0;
513 let mut stateless_scope_count = 0;
514
515 for entry in owned_scopes
516 .values()
517 .flat_map(|bucket| bucket.iter())
518 .chain(fast_scopes.values())
519 {
520 let entry = entry.borrow();
521 observed_state_count += entry.observed.len();
522 observed_state_capacity += entry.observed.capacity();
523 stateless_scope_count += usize::from(entry.observed.is_empty());
524 }
525
526 SnapshotStateObserverDebugStats {
527 scopes_len,
528 scopes_cap,
529 fast_scopes_len: fast_scopes.len(),
530 fast_scopes_cap: fast_scopes.capacity(),
531 stateless_scope_count,
532 observed_state_count,
533 observed_state_capacity,
534 }
535 }
536
537 fn run_with_read_observer<R>(&self, block: impl FnOnce() -> R) -> R {
538 use crate::snapshot_v2::take_transparent_observer_mutable_snapshot;
541
542 let snapshot =
545 take_transparent_observer_mutable_snapshot(Some(self.read_dispatcher.clone()), None);
546 let result = snapshot.enter(block);
547 snapshot.dispose();
548 result
549 }
550
551 fn handle_apply(&self, modified: &[Arc<dyn StateObject>]) {
552 if modified.is_empty() {
553 return;
554 }
555
556 let mut seen_scope_ids: HashSet<usize> = HashSet::default();
557 let mut to_notify: Vec<Rc<RefCell<ScopeEntry>>> = Vec::new();
558 {
559 let observed_to_scopes = self.observed_to_scopes.borrow();
560 let indexed_scopes = self.indexed_scopes.borrow();
561 for state in modified {
562 if let Some(scope_ids) = observed_to_scopes.get(&state.object_id().as_usize()) {
563 let mut ordered_scope_ids: SmallVec<[usize; 8]> =
564 scope_ids.iter().copied().collect();
565 ordered_scope_ids.sort_unstable();
566 for scope_id in ordered_scope_ids {
567 if seen_scope_ids.insert(scope_id) {
568 if let Some(entry) = indexed_scopes.get(&scope_id) {
569 to_notify.push(entry.clone());
570 }
571 }
572 }
573 }
574 }
575 }
576
577 if to_notify.is_empty() {
578 return;
579 }
580
581 for entry in to_notify {
582 let executor = self.executor.clone();
583 executor(Box::new(move || {
584 if let Ok(entry) = entry.try_borrow() {
585 entry.notify();
586 }
587 }));
588 }
589 }
590
591 fn replace_observed_ids(&self, entry: &Rc<RefCell<ScopeEntry>>, observed: ObservedIds) {
592 let (entry_id, previous) = {
593 let mut entry_mut = entry.borrow_mut();
594 let entry_id = entry_mut.id;
595 let previous = std::mem::replace(&mut entry_mut.observed, observed);
596 (entry_id, previous)
597 };
598 self.unregister_observed_ids(entry_id, &previous);
599 let entry_ref = entry.borrow();
600 self.register_observed_ids(entry_id, &entry_ref.observed);
601 }
602
603 fn register_observed_ids(&self, entry_id: usize, observed: &ObservedIds) {
604 let mut observed_to_scopes = self.observed_to_scopes.borrow_mut();
605 for state_id in observed.iter() {
606 let scope_ids = observed_to_scopes.entry(state_id).or_default();
607 scope_ids.insert(entry_id);
608 }
609 }
610
611 fn unregister_observed_ids(&self, entry_id: usize, observed: &ObservedIds) {
612 let mut observed_to_scopes = self.observed_to_scopes.borrow_mut();
613 let mut emptied = SmallVec::<[StateObjectId; MAX_OBSERVED_STATES]>::new();
614 for state_id in observed.iter() {
615 if let Some(scope_ids) = observed_to_scopes.get_mut(&state_id) {
616 scope_ids.remove(&entry_id);
617 if scope_ids.is_empty() {
618 emptied.push(state_id);
619 }
620 }
621 }
622 for state_id in emptied {
623 observed_to_scopes.remove(&state_id);
624 }
625 shrink_map_if_sparse(&mut observed_to_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
626 }
627
628 fn unregister_entry(&self, entry: &Rc<RefCell<ScopeEntry>>) {
629 let (entry_id, observed) = {
630 let mut entry_mut = entry.borrow_mut();
631 let observed = std::mem::replace(&mut entry_mut.observed, ObservedIds::new());
632 (entry_mut.id, observed)
633 };
634 self.unregister_observed_ids(entry_id, &observed);
635 self.indexed_scopes.borrow_mut().remove(&entry_id);
636 }
637}
638
639fn shrink_map_if_sparse<K, V>(map: &mut HashMap<K, V>, min_retained_capacity: usize)
640where
641 K: Eq + std::hash::Hash,
642{
643 if map.capacity() <= map.len().max(min_retained_capacity).saturating_mul(4) {
644 return;
645 }
646
647 let retained = map.len().max(min_retained_capacity);
648 let mut rebuilt = HashMap::default();
649 rebuilt.reserve(retained);
650 rebuilt.extend(map.drain());
651 *map = rebuilt;
652}
653
654enum ObservedIds {
655 Small(SmallVec<[ObservedState; MAX_OBSERVED_STATES]>),
656 Large(HashMap<StateObjectId, Option<Box<dyn Any>>>),
657}
658
659struct ObservedState {
660 id: StateObjectId,
661 _lease: Option<Box<dyn Any>>,
662}
663
664impl ObservedIds {
665 fn new() -> Self {
666 ObservedIds::Small(SmallVec::new())
667 }
668
669 fn insert(&mut self, state: &dyn StateObject) {
670 let id = state.object_id().as_usize();
671 match self {
672 ObservedIds::Small(small) => {
673 if small.iter().any(|observed| observed.id == id) {
674 return;
675 }
676 if small.len() < MAX_OBSERVED_STATES {
677 small.push(ObservedState {
678 id,
679 _lease: state.observation_lease(),
680 });
681 } else {
682 let mut large =
683 HashMap::with_capacity_and_hasher(small.len() + 1, Default::default());
684 for observed in small.drain(..) {
685 large.insert(observed.id, observed._lease);
686 }
687 large.insert(id, state.observation_lease());
688 *self = ObservedIds::Large(large);
689 }
690 }
691 ObservedIds::Large(large) => {
692 large.entry(id).or_insert_with(|| state.observation_lease());
695 }
696 }
697 }
698
699 fn is_empty(&self) -> bool {
700 match self {
701 ObservedIds::Small(small) => small.is_empty(),
702 ObservedIds::Large(large) => large.is_empty(),
703 }
704 }
705
706 fn len(&self) -> usize {
707 match self {
708 ObservedIds::Small(small) => small.len(),
709 ObservedIds::Large(large) => large.len(),
710 }
711 }
712
713 fn capacity(&self) -> usize {
714 match self {
715 ObservedIds::Small(small) => small.capacity(),
716 ObservedIds::Large(large) => large.capacity(),
717 }
718 }
719
720 fn iter(&self) -> Box<dyn Iterator<Item = StateObjectId> + '_> {
721 match self {
722 ObservedIds::Small(small) => Box::new(small.iter().map(|observed| observed.id)),
723 ObservedIds::Large(large) => Box::new(large.keys().copied()),
724 }
725 }
726}
727
728const MAX_OBSERVED_STATES: usize = 8;
729
730enum ScopeStorage {
731 Owned(Box<dyn Any>),
732 RecomposeScope {
733 id: ScopeId,
734 weak: Weak<RecomposeScopeInner>,
735 },
736}
737
738struct ScopeEntry {
739 id: usize,
740 scope: ScopeStorage,
741 on_changed: Rc<dyn Fn(&dyn Any)>,
742 observed: ObservedIds,
743 last_seen_version: u64,
744}
745
746impl ScopeEntry {
747 fn new<T>(id: usize, scope: T, on_changed: Rc<dyn Fn(&dyn Any)>) -> Self
748 where
749 T: Any + 'static,
750 {
751 Self {
752 id,
753 scope: ScopeStorage::from_value(scope),
754 on_changed,
755 observed: ObservedIds::new(),
756 last_seen_version: u64::MAX,
757 }
758 }
759
760 fn update<T>(&mut self, new_scope: T, on_changed: Rc<dyn Fn(&dyn Any)>)
761 where
762 T: Any + 'static,
763 {
764 self.scope = ScopeStorage::from_value(new_scope);
765 self.on_changed = on_changed;
766 }
767
768 fn matches_scope<T>(&self, scope: &T) -> bool
769 where
770 T: Any + Eq + 'static,
771 {
772 if let Some(scope) = (scope as &dyn Any).downcast_ref::<RecomposeScope>() {
773 return matches!(
774 &self.scope,
775 ScopeStorage::RecomposeScope { id, .. } if *id == scope.id()
776 );
777 }
778
779 match &self.scope {
780 ScopeStorage::Owned(stored) => stored
781 .downcast_ref::<T>()
782 .map(|stored| stored == scope)
783 .unwrap_or(false),
784 ScopeStorage::RecomposeScope { .. } => false,
785 }
786 }
787
788 fn matches_predicate(&self, predicate: &impl Fn(&dyn Any) -> bool) -> bool {
789 match &self.scope {
790 ScopeStorage::Owned(scope) => predicate(scope.as_ref()),
791 ScopeStorage::RecomposeScope { weak, .. } => weak
792 .upgrade()
793 .map(|inner| predicate(&RecomposeScope { inner }))
794 .unwrap_or(true),
795 }
796 }
797
798 fn should_retain(&self) -> bool {
799 match &self.scope {
800 ScopeStorage::Owned(_) => true,
801 ScopeStorage::RecomposeScope { weak, .. } => weak.upgrade().is_some(),
802 }
803 }
804
805 fn notify(&self) {
806 match &self.scope {
807 ScopeStorage::Owned(scope) => (self.on_changed)(scope.as_ref()),
808 ScopeStorage::RecomposeScope { weak, .. } => {
809 if let Some(inner) = weak.upgrade() {
810 (self.on_changed)(&RecomposeScope { inner });
811 }
812 }
813 }
814 }
815}
816
817impl ScopeStorage {
818 fn from_value<T>(value: T) -> Self
819 where
820 T: Any + 'static,
821 {
822 let any = &value as &dyn Any;
823 if let Some(scope) = any.downcast_ref::<RecomposeScope>() {
824 Self::RecomposeScope {
825 id: scope.id(),
826 weak: scope.downgrade(),
827 }
828 } else {
829 Self::Owned(Box::new(value))
830 }
831 }
832}
833
834#[cfg(test)]
835mod tests {
836 use std::cell::Cell;
837
838 use super::*;
839 use crate::{
840 snapshot_v2::{reset_runtime_for_tests, take_mutable_snapshot, TestRuntimeGuard},
841 state::{NeverEqual, SnapshotMutableState},
842 };
843
844 fn reset_runtime() -> TestRuntimeGuard {
845 reset_runtime_for_tests()
846 }
847
848 #[derive(Clone, Eq, Hash, PartialEq)]
849 struct TestScope(&'static str);
850
851 #[test]
852 fn notifies_scope_when_state_changes() {
853 let _guard = reset_runtime();
854
855 let state = SnapshotMutableState::new_in_arc(0, Arc::new(NeverEqual));
856 let triggered = Rc::new(Cell::new(0));
857 let observer_trigger = triggered.clone();
858
859 let observer = SnapshotStateObserver::new(|callback| callback());
860 observer.start();
861
862 let scope = TestScope("scope");
863 observer.observe_reads(
864 scope.clone(),
865 move |_| {
866 observer_trigger.set(observer_trigger.get() + 1);
867 },
868 || {
869 let _ = state.get();
870 },
871 );
872
873 let snapshot = take_mutable_snapshot(None, None);
874 snapshot.enter(|| {
875 state.set(1);
876 });
877 snapshot.apply().check();
878
879 assert_eq!(triggered.get(), 1);
880 observer.stop();
881 }
882
883 #[test]
884 fn clear_removes_scope_observation() {
885 let _guard = reset_runtime();
886
887 let state = SnapshotMutableState::new_in_arc(0, Arc::new(NeverEqual));
888 let triggered = Rc::new(Cell::new(0));
889 let observer_trigger = triggered.clone();
890
891 let observer = SnapshotStateObserver::new(|callback| callback());
892 observer.start();
893
894 let scope = TestScope("scope");
895 observer.observe_reads(
896 scope.clone(),
897 move |_| {
898 observer_trigger.set(observer_trigger.get() + 1);
899 },
900 || {
901 let _ = state.get();
902 },
903 );
904
905 observer.clear(&scope);
906
907 let snapshot = take_mutable_snapshot(None, None);
908 snapshot.enter(|| {
909 state.set(1);
910 });
911 snapshot.apply().check();
912
913 assert_eq!(triggered.get(), 0);
914 observer.stop();
915 }
916
917 #[test]
918 fn repeated_owned_scope_observations_reuse_the_same_entry() {
919 let _guard = reset_runtime();
920
921 let state = SnapshotMutableState::new_in_arc(0, Arc::new(NeverEqual));
922 let observer = SnapshotStateObserver::new(|callback| callback());
923 let scope = TestScope("scope");
924
925 observer.observe_reads(
926 scope.clone(),
927 |_| {},
928 || {
929 let _ = state.get();
930 },
931 );
932 observer.observe_reads(
933 scope,
934 |_| {},
935 || {
936 let _ = state.get();
937 },
938 );
939
940 let stats = observer.debug_stats();
941 assert_eq!(stats.scopes_len, 1);
942 assert_eq!(stats.fast_scopes_len, 0);
943 }
944
945 #[test]
946 fn with_no_observations_skips_reads() {
947 let _guard = reset_runtime();
948
949 let state = SnapshotMutableState::new_in_arc(0, Arc::new(NeverEqual));
950 let triggered = Rc::new(Cell::new(0));
951 let observer_trigger = triggered.clone();
952
953 let observer = SnapshotStateObserver::new(|callback| callback());
954 observer.start();
955
956 let scope = TestScope("scope");
957 observer.observe_reads(
958 scope.clone(),
959 move |_| {
960 observer_trigger.set(observer_trigger.get() + 1);
961 },
962 || {
963 observer.with_no_observations(|| {
964 let _ = state.get();
965 });
966 },
967 );
968
969 let snapshot = take_mutable_snapshot(None, None);
970 snapshot.enter(|| {
971 state.set(1);
972 });
973 snapshot.apply().check();
974
975 assert_eq!(triggered.get(), 0);
976 observer.stop();
977 }
978
979 #[test]
980 fn nested_observe_reads_attributes_state_to_innermost_scope_only() {
981 let _guard = reset_runtime();
982
983 let state = SnapshotMutableState::new_in_arc(0, Arc::new(NeverEqual));
984 let outer_triggered = Rc::new(Cell::new(0));
985 let inner_triggered = Rc::new(Cell::new(0));
986
987 let observer = SnapshotStateObserver::new(|callback| callback());
988 observer.start();
989
990 let outer_scope = TestScope("outer");
991 let inner_scope = TestScope("inner");
992 observer.observe_reads(
993 outer_scope.clone(),
994 {
995 let outer_triggered = Rc::clone(&outer_triggered);
996 move |_| outer_triggered.set(outer_triggered.get() + 1)
997 },
998 || {
999 observer.observe_reads(
1000 inner_scope.clone(),
1001 {
1002 let inner_triggered = Rc::clone(&inner_triggered);
1003 move |_| inner_triggered.set(inner_triggered.get() + 1)
1004 },
1005 || {
1006 let _ = state.get();
1007 },
1008 );
1009 },
1010 );
1011
1012 let snapshot = take_mutable_snapshot(None, None);
1013 snapshot.enter(|| {
1014 state.set(1);
1015 });
1016 snapshot.apply().check();
1017
1018 assert_eq!(outer_triggered.get(), 0);
1019 assert_eq!(inner_triggered.get(), 1);
1020 observer.stop();
1021 }
1022
1023 #[test]
1024 fn clearing_one_scope_keeps_shared_state_registered_for_other_scope() {
1025 let _guard = reset_runtime();
1026
1027 let state = SnapshotMutableState::new_in_arc(0, Arc::new(NeverEqual));
1028 let first_triggered = Rc::new(Cell::new(0));
1029 let second_triggered = Rc::new(Cell::new(0));
1030
1031 let observer = SnapshotStateObserver::new(|callback| callback());
1032 observer.start();
1033
1034 let first_scope = TestScope("first");
1035 let second_scope = TestScope("second");
1036 observer.observe_reads(
1037 first_scope.clone(),
1038 {
1039 let first_triggered = Rc::clone(&first_triggered);
1040 move |_| first_triggered.set(first_triggered.get() + 1)
1041 },
1042 || {
1043 let _ = state.get();
1044 },
1045 );
1046 observer.observe_reads(
1047 second_scope.clone(),
1048 {
1049 let second_triggered = Rc::clone(&second_triggered);
1050 move |_| second_triggered.set(second_triggered.get() + 1)
1051 },
1052 || {
1053 let _ = state.get();
1054 },
1055 );
1056
1057 observer.clear(&first_scope);
1058
1059 let snapshot = take_mutable_snapshot(None, None);
1060 snapshot.enter(|| {
1061 state.set(1);
1062 });
1063 snapshot.apply().check();
1064
1065 assert_eq!(first_triggered.get(), 0);
1066 assert_eq!(second_triggered.get(), 1);
1067 observer.stop();
1068 }
1069
1070 #[test]
1071 fn shared_state_notifies_scopes_in_registration_order() {
1072 let _guard = reset_runtime();
1073
1074 let state = SnapshotMutableState::new_in_arc(0, Arc::new(NeverEqual));
1075 let notifications = Rc::new(RefCell::new(Vec::new()));
1076
1077 let observer = SnapshotStateObserver::new(|callback| callback());
1078 observer.start();
1079
1080 observer.observe_reads(
1081 TestScope("first"),
1082 {
1083 let notifications = Rc::clone(¬ifications);
1084 move |_| notifications.borrow_mut().push("first")
1085 },
1086 || {
1087 let _ = state.get();
1088 },
1089 );
1090 observer.observe_reads(
1091 TestScope("second"),
1092 {
1093 let notifications = Rc::clone(¬ifications);
1094 move |_| notifications.borrow_mut().push("second")
1095 },
1096 || {
1097 let _ = state.get();
1098 },
1099 );
1100
1101 let snapshot = take_mutable_snapshot(None, None);
1102 snapshot.enter(|| {
1103 state.set(1);
1104 });
1105 snapshot.apply().check();
1106
1107 assert_eq!(notifications.borrow().as_slice(), &["first", "second"]);
1108 observer.stop();
1109 }
1110
1111 #[test]
1112 fn stateless_recompose_scope_does_not_retain_observer_entry() {
1113 let _guard = reset_runtime();
1114
1115 let observer = SnapshotStateObserver::new(|callback| callback());
1116 let runtime = crate::TestRuntime::new();
1117 let scope = RecomposeScope::new_for_test(runtime.handle());
1118
1119 observer.observe_reads(scope, |_| {}, || {});
1120
1121 let stats = observer.debug_stats();
1122 assert_eq!(stats.scopes_len, 0);
1123 assert_eq!(stats.fast_scopes_len, 0);
1124 assert_eq!(stats.stateless_scope_count, 0);
1125 }
1126
1127 #[test]
1128 fn scope_that_stops_reading_state_is_removed_immediately() {
1129 let _guard = reset_runtime();
1130
1131 let state = SnapshotMutableState::new_in_arc(0, Arc::new(NeverEqual));
1132 let observer = SnapshotStateObserver::new(|callback| callback());
1133 let runtime = crate::TestRuntime::new();
1134 let scope = RecomposeScope::new_for_test(runtime.handle());
1135 let triggered = Rc::new(Cell::new(0));
1136 let observer_trigger = Rc::clone(&triggered);
1137
1138 observer.observe_reads(
1139 scope.clone(),
1140 move |_| observer_trigger.set(observer_trigger.get() + 1),
1141 || {
1142 let _ = state.get();
1143 },
1144 );
1145
1146 let after_stateful = observer.debug_stats();
1147 assert_eq!(after_stateful.scopes_len, 1);
1148 assert_eq!(after_stateful.fast_scopes_len, 1);
1149
1150 observer.observe_reads(scope, |_| {}, || {});
1151
1152 let after_stateless = observer.debug_stats();
1153 assert_eq!(after_stateless.scopes_len, 0);
1154 assert_eq!(after_stateless.fast_scopes_len, 0);
1155
1156 let snapshot = take_mutable_snapshot(None, None);
1157 snapshot.enter(|| {
1158 state.set(1);
1159 });
1160 snapshot.apply().check();
1161
1162 assert_eq!(triggered.get(), 0);
1163 }
1164
1165 #[test]
1166 fn begin_frame_prunes_dropped_recompose_scope_entries() {
1167 let _guard = reset_runtime();
1168
1169 let state = SnapshotMutableState::new_in_arc(0, Arc::new(NeverEqual));
1170 let observer = SnapshotStateObserver::new(|callback| callback());
1171 let runtime = crate::TestRuntime::new();
1172 let scope = RecomposeScope::new_for_test(runtime.handle());
1173
1174 observer.observe_reads(
1175 scope.clone(),
1176 |_| {},
1177 || {
1178 let _ = state.get();
1179 },
1180 );
1181
1182 let before_prune = observer.debug_stats();
1183 assert_eq!(before_prune.scopes_len, 1);
1184 assert_eq!(before_prune.fast_scopes_len, 1);
1185
1186 drop(scope);
1187 observer.begin_frame();
1188
1189 let after_prune = observer.debug_stats();
1190 assert_eq!(after_prune.scopes_len, 0);
1191 assert_eq!(after_prune.fast_scopes_len, 0);
1192 }
1193}