1use std::{
2 any::{Any, TypeId},
3 cell::{Cell, RefCell},
4 hash::{Hash, Hasher},
5 rc::{Rc, Weak},
6 sync::Arc,
7};
8
9use smallvec::SmallVec;
10
11use crate::{
12 RecomposeScope, RecomposeScopeInner, ScopeId,
13 collections::map::{HashMap, HashSet},
14 hash::default as default_hash,
15 snapshot_v2::{
16 ReadObserver, StateObjectId, TransparentObserverMutableSnapshot, register_apply_observer,
17 },
18 state::StateObject,
19};
20
21type Executor = dyn Fn(Box<dyn FnOnce() + 'static>) + 'static;
22
23trait ScopeChangedCallback: Fn(&dyn Any) + Any {}
24
25impl<F: Fn(&dyn Any) + Any> ScopeChangedCallback for F {}
26
27#[derive(Clone)]
40pub struct SnapshotStateObserver {
41 inner: Rc<SnapshotStateObserverInner>,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
45pub struct SnapshotStateObserverDebugStats {
46 pub scopes_len: usize,
47 pub scopes_cap: usize,
48 pub recompose_scopes_len: usize,
49 pub stateless_scope_count: usize,
50 pub observed_state_count: usize,
51 pub observed_state_capacity: usize,
52}
53
54impl SnapshotStateObserver {
55 pub fn new(on_changed_executor: impl Fn(Box<dyn FnOnce() + 'static>) + 'static) -> Self {
57 let inner = Rc::new(SnapshotStateObserverInner::new(on_changed_executor));
58 inner.set_self(Rc::downgrade(&inner));
59 Self { inner }
60 }
61
62 pub fn observe_reads<T, R>(
68 &self,
69 scope: T,
70 on_value_changed_for_scope: impl Fn(&T) + 'static,
71 block: impl FnOnce() -> R,
72 ) -> R
73 where
74 T: Any + Clone + Eq + Hash + 'static,
75 {
76 self.inner
77 .observe_reads(scope, on_value_changed_for_scope, block)
78 }
79
80 pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
81 Rc::ptr_eq(&self.inner, &other.inner)
82 }
83
84 pub fn begin_frame(&self) {
86 self.inner.begin_frame();
87 }
88
89 pub fn prune_dead_scopes(&self) {
91 self.inner.prune_dead_scopes();
92 }
93
94 pub fn with_no_observations<R>(&self, block: impl FnOnce() -> R) -> R {
96 self.inner.with_no_observations(block)
97 }
98
99 pub fn clear<T>(&self, scope: &T)
101 where
102 T: Any + Eq + Hash + 'static,
103 {
104 self.inner.clear(scope);
105 }
106
107 pub fn clear_if(&self, predicate: impl Fn(&dyn Any) -> bool) {
109 self.inner.clear_if(predicate);
110 }
111
112 pub fn clear_all(&self) {
114 self.inner.clear_all();
115 }
116
117 pub fn start(&self) {
119 let weak = Rc::downgrade(&self.inner);
120 self.inner.start(weak);
121 }
122
123 pub fn stop(&self) {
125 self.inner.stop();
126 }
127
128 pub fn debug_stats(&self) -> SnapshotStateObserverDebugStats {
129 self.inner.debug_stats()
130 }
131
132 #[cfg(test)]
133 pub fn notify_changes(&self, modified: &[Arc<dyn StateObject>]) {
134 self.inner.handle_apply(modified);
135 }
136}
137
138struct SnapshotStateObserverInner {
139 executor: Rc<Executor>,
140 owned_scopes: RefCell<HashMap<OwnedScopeIndexKey, OwnedScopeBucket>>,
141 indexed_scopes: RefCell<HashMap<usize, Rc<RefCell<ScopeEntry>>>>,
142 observed_to_scopes: RefCell<HashMap<StateObjectId, HashSet<usize>>>,
143 pause_count: Rc<Cell<usize>>,
144 active_read_targets: Rc<RefCell<ReadObservationStack>>,
145 read_dispatcher: ReadObserver,
146 read_snapshot: RefCell<Option<Arc<TransparentObserverMutableSnapshot>>>,
147 apply_handle: RefCell<Option<crate::snapshot_v2::ObserverHandle>>,
148 weak_self: RefCell<Weak<SnapshotStateObserverInner>>,
149 frame_version: Cell<u64>,
150 dead_entries: RefCell<Vec<Rc<RefCell<ScopeEntry>>>>,
151 next_entry_id: Cell<usize>,
152 capture_free_callbacks: RefCell<CaptureFreeCallbacks>,
155}
156
157#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
158struct OwnedScopeIndexKey {
159 type_id: TypeId,
160 value_hash: u64,
161}
162
163type OwnedScopeBucket = SmallVec<[Rc<RefCell<ScopeEntry>>; 1]>;
164
165type CaptureFreeCallbacks = SmallVec<[(TypeId, Rc<dyn ScopeChangedCallback>); 2]>;
167
168fn owned_scope_index_key<T>(scope: &T) -> OwnedScopeIndexKey
169where
170 T: Any + Hash + 'static,
171{
172 let mut hasher = default_hash::new();
173 scope.hash(&mut hasher);
174 OwnedScopeIndexKey {
175 type_id: TypeId::of::<T>(),
176 value_hash: hasher.finish(),
177 }
178}
179
180impl SnapshotStateObserverInner {
181 const MIN_RETAINED_SCOPE_CAPACITY: usize = 256;
182
183 fn new(on_changed_executor: impl Fn(Box<dyn FnOnce() + 'static>) + 'static) -> Self {
184 let pause_count = Rc::new(Cell::new(0));
185 let active_read_targets = Rc::new(RefCell::new(ReadObservationStack::default()));
186 let dispatcher_pause_count = Rc::clone(&pause_count);
187 let dispatcher_targets = Rc::clone(&active_read_targets);
188 let read_dispatcher: ReadObserver = Arc::new(move |state| {
189 if dispatcher_pause_count.get() > 0 {
190 return;
191 }
192 let observed = dispatcher_targets.borrow().last().cloned();
193 if let Some(observed) = observed {
194 observed.borrow_mut().insert(state);
195 }
196 });
197
198 Self {
199 executor: Rc::new(on_changed_executor),
200 owned_scopes: RefCell::new(HashMap::default()),
201 indexed_scopes: RefCell::new(HashMap::default()),
202 observed_to_scopes: RefCell::new(HashMap::default()),
203 pause_count,
204 active_read_targets,
205 read_dispatcher,
206 read_snapshot: RefCell::new(None),
207 apply_handle: RefCell::new(None),
208 weak_self: RefCell::new(Weak::new()),
209 frame_version: Cell::new(0),
210 dead_entries: RefCell::new(Vec::new()),
211 next_entry_id: Cell::new(0),
212 capture_free_callbacks: RefCell::new(SmallVec::new()),
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 existing_entry = self.find_scope_entry(&scope);
239 let on_changed = std::cell::LazyCell::new(|| {
240 let callback = move |scope_any: &dyn Any| {
241 if let Some(typed) = scope_any.downcast_ref::<T>() {
242 on_value_changed_for_scope(typed);
243 }
244 };
245 if std::mem::size_of_val(&callback) == 0 {
246 return self.capture_free_callback(callback);
247 }
248 match existing_entry.as_ref() {
249 Some(entry) => entry.borrow_mut().callback_reusing(callback),
250 None => Rc::new(callback),
251 }
252 });
253
254 if let Some(entry) = existing_entry.as_ref() {
255 let already_observed = {
256 let mut entry_mut = entry.borrow_mut();
257 entry_mut.update_scope(&scope);
258 has_frame_version && entry_mut.last_seen_version == frame_version
259 };
260 let callback = on_changed.clone();
261 entry.borrow_mut().on_changed = callback;
262 if already_observed {
263 return block();
264 }
265 }
266
267 let observed = self.active_read_targets.borrow_mut().push();
268 struct ActiveObservationGuard<'a> {
269 stack: &'a RefCell<ReadObservationStack>,
270 }
271 impl Drop for ActiveObservationGuard<'_> {
272 fn drop(&mut self) {
273 let target = self.stack.borrow_mut().pop();
274 target.borrow_mut().clear();
275 }
276 }
277 let _guard = ActiveObservationGuard {
278 stack: &self.active_read_targets,
279 };
280
281 let result = self.run_with_read_observer(block);
282
283 if observed.borrow().is_empty() {
284 if existing_entry.is_some() {
285 self.clear(&scope);
286 }
287 return result;
288 }
289
290 let callback = Rc::clone(&on_changed);
291 drop(on_changed);
292 let entry = existing_entry
293 .unwrap_or_else(|| self.insert_scope_entry(scope.clone(), Rc::clone(&callback)));
294 {
295 let mut entry_mut = entry.borrow_mut();
296 entry_mut.update(&scope, callback);
297 entry_mut.last_seen_version = if has_frame_version {
298 frame_version
299 } else {
300 u64::MAX
301 };
302 }
303 self.replace_observed_ids(&entry, &mut observed.borrow_mut());
304
305 result
306 }
307
308 fn with_no_observations<R>(&self, block: impl FnOnce() -> R) -> R {
309 self.pause_count.set(self.pause_count.get() + 1);
310 let result = block();
311 self.pause_count
312 .set(self.pause_count.get().saturating_sub(1));
313 result
314 }
315
316 fn clear<T>(&self, scope: &T)
317 where
318 T: Any + Eq + Hash + 'static,
319 {
320 if let Some(rc_scope) = (scope as &dyn Any).downcast_ref::<RecomposeScope>() {
321 if let Some(entry) = rc_scope.inner.observers.unlink(self) {
322 self.unregister_entry(&entry);
323 }
324 return;
325 }
326
327 let removed = self.remove_owned_scope_entry(scope);
328 if let Some(entry) = removed {
329 self.unregister_entry(&entry);
330 }
331 }
332
333 fn clear_if(&self, predicate: impl Fn(&dyn Any) -> bool) {
334 let removed_recompose: Vec<_> = self
335 .indexed_scopes
336 .borrow()
337 .values()
338 .filter(|entry| {
339 let entry = entry.borrow();
340 entry.holds_recompose_scope() && entry.matches_predicate(&predicate)
341 })
342 .cloned()
343 .collect();
344 for entry in &removed_recompose {
345 self.unlink_entry(entry);
346 }
347 let removed_owned =
348 { self.partition_owned_scopes(|entry| entry.matches_predicate(&predicate)) };
349
350 for entry in removed_recompose.into_iter().chain(removed_owned) {
351 self.unregister_entry(&entry);
352 }
353 }
354
355 fn clear_all(&self) {
356 let entries = std::mem::take(&mut *self.indexed_scopes.borrow_mut());
357 for entry in entries.values() {
358 self.unlink_entry(entry);
359 }
360 let owned = std::mem::take(&mut *self.owned_scopes.borrow_mut());
361 self.observed_to_scopes.borrow_mut().clear();
362 let dead = std::mem::take(&mut *self.dead_entries.borrow_mut());
363 drop((entries, owned, dead));
364 }
365
366 fn unlink_entry(&self, entry: &Rc<RefCell<ScopeEntry>>) {
367 let scope = entry.borrow().live_recompose_scope();
368 if let Some(scope) = scope {
369 scope.inner.observers.unlink(self);
370 }
371 }
372
373 fn start(&self, weak_self: Weak<SnapshotStateObserverInner>) {
374 if self.apply_handle.borrow().is_some() {
375 return;
376 }
377
378 let handle = register_apply_observer(Rc::new(move |modified, _snapshot_id| {
379 if let Some(inner) = weak_self.upgrade() {
380 inner.handle_apply(modified);
381 }
382 }));
383 self.apply_handle.replace(Some(handle));
384 }
385
386 fn stop(&self) {
387 if let Some(handle) = self.apply_handle.borrow_mut().take() {
388 drop(handle);
389 }
390 }
391
392 fn find_scope_entry<T>(&self, scope: &T) -> Option<Rc<RefCell<ScopeEntry>>>
393 where
394 T: Any + Eq + Hash + 'static,
395 {
396 if let Some(scope) = (scope as &dyn Any).downcast_ref::<RecomposeScope>() {
397 return scope.inner.observers.find(self);
398 }
399
400 self.find_owned_scope_entry(scope)
401 }
402
403 fn capture_free_callback<F: Fn(&dyn Any) + 'static>(
407 &self,
408 callback: F,
409 ) -> Rc<dyn ScopeChangedCallback> {
410 let type_id = TypeId::of::<F>();
411 let mut shared = self.capture_free_callbacks.borrow_mut();
412 if let Some((_, callback)) = shared.iter().find(|(id, _)| *id == type_id) {
413 return Rc::clone(callback);
414 }
415 let callback: Rc<dyn ScopeChangedCallback> = Rc::new(callback);
416 shared.push((type_id, Rc::clone(&callback)));
417 callback
418 }
419
420 fn insert_scope_entry(
421 &self,
422 scope: impl Any + Clone + Eq + Hash + 'static,
423 on_changed: Rc<dyn ScopeChangedCallback>,
424 ) -> Rc<RefCell<ScopeEntry>> {
425 let entry_id = self.next_entry_id.get();
426 self.next_entry_id.set(entry_id.wrapping_add(1));
427 let recompose_scope = (&scope as &dyn Any)
428 .downcast_ref::<RecomposeScope>()
429 .cloned();
430 let owned_scope_key = recompose_scope
431 .is_none()
432 .then(|| owned_scope_index_key(&scope));
433 let entry = Rc::new(RefCell::new(ScopeEntry::new(entry_id, scope, on_changed)));
434 self.indexed_scopes
435 .borrow_mut()
436 .insert(entry_id, Rc::clone(&entry));
437 if let Some(recompose_scope) = recompose_scope {
438 recompose_scope
439 .inner
440 .observers
441 .link(self.weak_self.borrow().clone(), Rc::clone(&entry));
442 } else if let Some(scope_key) = owned_scope_key {
443 self.owned_scopes
444 .borrow_mut()
445 .entry(scope_key)
446 .or_default()
447 .push(Rc::clone(&entry));
448 }
449 entry
450 }
451
452 fn prune_dead_scopes(&self) {
453 while let Some(entry) = self.pop_dead_entry() {
454 self.unregister_entry(&entry);
455 }
456 }
457
458 fn pop_dead_entry(&self) -> Option<Rc<RefCell<ScopeEntry>>> {
459 self.dead_entries.borrow_mut().pop()
460 }
461
462 fn find_owned_scope_entry<T>(&self, scope: &T) -> Option<Rc<RefCell<ScopeEntry>>>
463 where
464 T: Any + Eq + Hash + 'static,
465 {
466 let key = owned_scope_index_key(scope);
467 self.owned_scopes.borrow().get(&key).and_then(|bucket| {
468 bucket
469 .iter()
470 .find(|entry| entry.borrow().matches_scope(scope))
471 .cloned()
472 })
473 }
474
475 fn remove_owned_scope_entry<T>(&self, scope: &T) -> Option<Rc<RefCell<ScopeEntry>>>
476 where
477 T: Any + Eq + Hash + 'static,
478 {
479 let key = owned_scope_index_key(scope);
480 let mut owned_scopes = self.owned_scopes.borrow_mut();
481 let mut removed = None;
482 let mut remove_bucket = false;
483 if let Some(bucket) = owned_scopes.get_mut(&key)
484 && let Some(index) = bucket
485 .iter()
486 .position(|entry| entry.borrow().matches_scope(scope))
487 {
488 removed = Some(bucket.remove(index));
489 remove_bucket = bucket.is_empty();
490 }
491 if remove_bucket {
492 owned_scopes.remove(&key);
493 }
494 shrink_map_if_sparse(&mut owned_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
495 removed
496 }
497
498 fn partition_owned_scopes(
499 &self,
500 should_remove: impl Fn(&ScopeEntry) -> bool,
501 ) -> Vec<Rc<RefCell<ScopeEntry>>> {
502 let mut owned_scopes = self.owned_scopes.borrow_mut();
503 let mut retained = HashMap::default();
504 let mut removed = Vec::new();
505 for (key, mut bucket) in owned_scopes.drain() {
506 let mut retained_bucket = OwnedScopeBucket::new();
507 for entry in bucket.drain(..) {
508 if should_remove(&entry.borrow()) {
509 removed.push(entry);
510 } else {
511 retained_bucket.push(entry);
512 }
513 }
514 if !retained_bucket.is_empty() {
515 retained.insert(key, retained_bucket);
516 }
517 }
518 *owned_scopes = retained;
519 shrink_map_if_sparse(&mut owned_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
520 removed
521 }
522
523 fn debug_stats(&self) -> SnapshotStateObserverDebugStats {
524 let owned_scopes = self.owned_scopes.borrow();
525 let indexed_scopes = self.indexed_scopes.borrow();
526 let owned_scope_cap =
527 owned_scopes.capacity() + owned_scopes.values().map(SmallVec::capacity).sum::<usize>();
528 let scopes_cap = owned_scope_cap + indexed_scopes.capacity();
529 let mut observed_state_count = 0;
530 let mut observed_state_capacity = 0;
531 let mut stateless_scope_count = 0;
532 let mut recompose_scopes_len = 0;
533
534 for entry in indexed_scopes.values() {
535 let entry = entry.borrow();
536 observed_state_count += entry.observed.len();
537 observed_state_capacity += entry.observed.capacity();
538 stateless_scope_count += usize::from(entry.observed.is_empty());
539 recompose_scopes_len += usize::from(entry.holds_recompose_scope());
540 }
541
542 SnapshotStateObserverDebugStats {
543 scopes_len: indexed_scopes.len(),
544 scopes_cap,
545 recompose_scopes_len,
546 stateless_scope_count,
547 observed_state_count,
548 observed_state_capacity,
549 }
550 }
551
552 fn run_with_read_observer<R>(&self, block: impl FnOnce() -> R) -> R {
553 use crate::snapshot_v2::{
554 current_snapshot_reads_into, take_transparent_observer_mutable_snapshot_reusing,
555 };
556
557 if current_snapshot_reads_into(&self.read_dispatcher) {
558 return block();
559 }
560
561 let mut snapshot = take_transparent_observer_mutable_snapshot_reusing(
562 Some(self.read_dispatcher.clone()),
563 None,
564 self.read_snapshot.take(),
565 );
566 let result = snapshot.enter(block);
567 snapshot.dispose();
568 if Arc::get_mut(&mut snapshot).is_some() && !snapshot.has_pending_changes() {
569 self.read_snapshot.replace(Some(snapshot));
570 }
571 result
572 }
573
574 fn handle_apply(&self, modified: &[Arc<dyn StateObject>]) {
575 if modified.is_empty() {
576 return;
577 }
578
579 let mut seen_scope_ids: HashSet<usize> = HashSet::default();
580 let mut to_notify: Vec<Rc<RefCell<ScopeEntry>>> = Vec::new();
581 {
582 let observed_to_scopes = self.observed_to_scopes.borrow();
583 let indexed_scopes = self.indexed_scopes.borrow();
584 for state in modified {
585 if let Some(scope_ids) = observed_to_scopes.get(&state.object_id().as_usize()) {
586 let mut ordered_scope_ids: SmallVec<[usize; 8]> =
587 scope_ids.iter().copied().collect();
588 ordered_scope_ids.sort_unstable();
589 for scope_id in ordered_scope_ids {
590 if seen_scope_ids.insert(scope_id)
591 && let Some(entry) = indexed_scopes.get(&scope_id)
592 {
593 to_notify.push(entry.clone());
594 }
595 }
596 }
597 }
598 }
599
600 if to_notify.is_empty() {
601 return;
602 }
603
604 for entry in to_notify {
605 let executor = self.executor.clone();
606 executor(Box::new(move || {
607 if let Ok(entry) = entry.try_borrow() {
608 entry.notify();
609 }
610 }));
611 }
612 }
613
614 fn replace_observed_ids(&self, entry: &Rc<RefCell<ScopeEntry>>, collected: &mut ObservedIds) {
615 let (entry_id, previous) = {
616 let mut entry_mut = entry.borrow_mut();
617 if entry_mut.observed.iter().eq(collected.iter()) {
618 entry_mut.observed.take_leases(collected);
619 collected.clear();
620 return;
621 }
622 let entry_id = entry_mut.id;
623 let previous = std::mem::replace(&mut entry_mut.observed, collected.take_sized());
624 (entry_id, previous)
625 };
626 let entry_ref = entry.borrow();
627 self.unregister_observed_ids(entry_id, &previous);
628 self.register_observed_ids(entry_id, &entry_ref.observed);
629 }
630
631 fn register_observed_ids(&self, entry_id: usize, observed: &ObservedIds) {
632 let mut observed_to_scopes = self.observed_to_scopes.borrow_mut();
633 for state_id in observed.iter() {
634 let scope_ids = observed_to_scopes.entry(state_id).or_default();
635 scope_ids.insert(entry_id);
636 }
637 }
638
639 fn unregister_observed_ids(&self, entry_id: usize, observed: &ObservedIds) {
640 let mut observed_to_scopes = self.observed_to_scopes.borrow_mut();
641 let mut emptied = SmallVec::<[StateObjectId; MAX_OBSERVED_STATES]>::new();
642 for state_id in observed.iter() {
643 if let Some(scope_ids) = observed_to_scopes.get_mut(&state_id) {
644 scope_ids.remove(&entry_id);
645 if scope_ids.is_empty() {
646 emptied.push(state_id);
647 }
648 }
649 }
650 for state_id in emptied {
651 observed_to_scopes.remove(&state_id);
652 }
653 shrink_map_if_sparse(&mut observed_to_scopes, Self::MIN_RETAINED_SCOPE_CAPACITY);
654 }
655
656 fn unregister_entry(&self, entry: &Rc<RefCell<ScopeEntry>>) {
657 let (entry_id, observed) = {
658 let mut entry_mut = entry.borrow_mut();
659 let observed = std::mem::replace(&mut entry_mut.observed, ObservedIds::new());
660 (entry_mut.id, observed)
661 };
662 self.unregister_observed_ids(entry_id, &observed);
663 self.indexed_scopes.borrow_mut().remove(&entry_id);
664 }
665}
666
667fn shrink_map_if_sparse<K, V>(map: &mut HashMap<K, V>, min_retained_capacity: usize)
668where
669 K: Eq + std::hash::Hash,
670{
671 if map.capacity() <= map.len().max(min_retained_capacity).saturating_mul(4) {
672 return;
673 }
674
675 let retained = map.len().max(min_retained_capacity);
676 let mut rebuilt = HashMap::default();
677 rebuilt.reserve(retained);
678 rebuilt.extend(map.drain());
679 *map = rebuilt;
680}
681
682#[derive(Default)]
683struct ReadObservationStack {
684 targets: Vec<Rc<RefCell<ObservedIds>>>,
685 depth: usize,
686}
687
688impl ReadObservationStack {
689 fn push(&mut self) -> Rc<RefCell<ObservedIds>> {
690 if self.depth == self.targets.len() {
691 self.targets.push(Rc::new(RefCell::new(ObservedIds::new())));
692 }
693 let target = Rc::clone(&self.targets[self.depth]);
694 self.depth += 1;
695 target
696 }
697
698 fn last(&self) -> Option<&Rc<RefCell<ObservedIds>>> {
699 self.depth.checked_sub(1).map(|index| &self.targets[index])
700 }
701
702 fn pop(&mut self) -> Rc<RefCell<ObservedIds>> {
703 self.depth -= 1;
704 Rc::clone(&self.targets[self.depth])
705 }
706}
707
708enum ObservedIds {
709 Small(SmallVec<[ObservedState; 1]>),
710 Large(Box<HashMap<StateObjectId, Option<Rc<dyn Any>>>>),
711}
712
713struct ObservedState {
714 id: StateObjectId,
715 _lease: Option<Rc<dyn Any>>,
716}
717
718impl ObservedIds {
719 fn new() -> Self {
720 ObservedIds::Small(SmallVec::new())
721 }
722
723 fn insert(&mut self, state: &dyn StateObject) {
724 let id = state.object_id().as_usize();
725 match self {
726 ObservedIds::Small(small) => {
727 if small.iter().any(|observed| observed.id == id) {
728 return;
729 }
730 if small.len() < MAX_OBSERVED_STATES {
731 small.push(ObservedState {
732 id,
733 _lease: state.observation_lease(),
734 });
735 } else {
736 let mut large =
737 HashMap::with_capacity_and_hasher(small.len() + 1, Default::default());
738 for observed in small.drain(..) {
739 large.insert(observed.id, observed._lease);
740 }
741 large.insert(id, state.observation_lease());
742 *self = ObservedIds::Large(Box::new(large));
743 }
744 }
745 ObservedIds::Large(large) => {
746 large.entry(id).or_insert_with(|| state.observation_lease());
747 }
748 }
749 }
750
751 fn clear(&mut self) {
752 match self {
753 ObservedIds::Small(small) => small.clear(),
754 ObservedIds::Large(_) => *self = ObservedIds::new(),
755 }
756 }
757
758 fn take_leases(&mut self, fresh: &mut ObservedIds) {
763 match (self, fresh) {
764 (ObservedIds::Small(kept), ObservedIds::Small(fresh)) => {
765 for (kept, fresh) in kept.iter_mut().zip(fresh.iter_mut()) {
766 std::mem::swap(&mut kept._lease, &mut fresh._lease);
767 }
768 }
769 (kept, fresh) => std::mem::swap(kept, fresh),
770 }
771 }
772
773 fn take_sized(&mut self) -> ObservedIds {
774 match self {
775 ObservedIds::Small(small) => ObservedIds::Small(small.drain(..).collect()),
776 ObservedIds::Large(_) => std::mem::replace(self, ObservedIds::new()),
777 }
778 }
779
780 fn is_empty(&self) -> bool {
781 match self {
782 ObservedIds::Small(small) => small.is_empty(),
783 ObservedIds::Large(large) => large.is_empty(),
784 }
785 }
786
787 fn len(&self) -> usize {
788 match self {
789 ObservedIds::Small(small) => small.len(),
790 ObservedIds::Large(large) => large.len(),
791 }
792 }
793
794 fn capacity(&self) -> usize {
795 match self {
796 ObservedIds::Small(small) => small.capacity(),
797 ObservedIds::Large(large) => large.capacity(),
798 }
799 }
800
801 fn iter(&self) -> impl Iterator<Item = StateObjectId> + '_ {
802 let (small, large) = match self {
803 ObservedIds::Small(small) => (Some(small.as_slice()), None),
804 ObservedIds::Large(large) => (None, Some(large)),
805 };
806 small
807 .into_iter()
808 .flatten()
809 .map(|observed| observed.id)
810 .chain(large.into_iter().flat_map(|states| states.keys().copied()))
811 }
812}
813
814const MAX_OBSERVED_STATES: usize = 8;
815
816enum ScopeStorage {
817 Owned(Box<dyn Any>),
818 RecomposeScope {
819 id: ScopeId,
820 weak: Weak<RecomposeScopeInner>,
821 },
822}
823
824struct ScopeEntry {
825 id: usize,
826 scope: ScopeStorage,
827 on_changed: Rc<dyn ScopeChangedCallback>,
828 observed: ObservedIds,
829 last_seen_version: u64,
830}
831
832impl ScopeEntry {
833 fn new<T>(id: usize, scope: T, on_changed: Rc<dyn ScopeChangedCallback>) -> Self
834 where
835 T: Any + 'static,
836 {
837 Self {
838 id,
839 scope: ScopeStorage::from_value(scope),
840 on_changed,
841 observed: ObservedIds::new(),
842 last_seen_version: u64::MAX,
843 }
844 }
845
846 fn callback_reusing<F: Fn(&dyn Any) + 'static>(
847 &mut self,
848 callback: F,
849 ) -> Rc<dyn ScopeChangedCallback> {
850 if let Some(stored) = Rc::get_mut(&mut self.on_changed)
851 .and_then(|stored| (stored as &mut dyn Any).downcast_mut::<F>())
852 {
853 *stored = callback;
854 Rc::clone(&self.on_changed)
855 } else {
856 Rc::new(callback)
857 }
858 }
859
860 fn update<T>(&mut self, new_scope: &T, on_changed: Rc<dyn ScopeChangedCallback>)
861 where
862 T: Any + Clone + 'static,
863 {
864 self.update_scope(new_scope);
865 self.on_changed = on_changed;
866 }
867
868 fn update_scope<T>(&mut self, new_scope: &T)
869 where
870 T: Any + Clone + 'static,
871 {
872 match &mut self.scope {
873 ScopeStorage::Owned(stored) => {
874 if let Some(stored) = stored.downcast_mut::<T>() {
875 stored.clone_from(new_scope);
876 return;
877 }
878 }
879 ScopeStorage::RecomposeScope { weak, .. } => {
880 if (new_scope as &dyn Any)
881 .downcast_ref::<RecomposeScope>()
882 .is_some_and(|scope| Weak::as_ptr(weak) == Rc::as_ptr(&scope.inner))
883 {
884 return;
885 }
886 }
887 }
888 self.scope = ScopeStorage::from_value(new_scope.clone());
889 }
890
891 fn matches_scope<T>(&self, scope: &T) -> bool
892 where
893 T: Any + Eq + 'static,
894 {
895 if let Some(scope) = (scope as &dyn Any).downcast_ref::<RecomposeScope>() {
896 return matches!(
897 &self.scope,
898 ScopeStorage::RecomposeScope { id, .. } if *id == scope.id()
899 );
900 }
901
902 match &self.scope {
903 ScopeStorage::Owned(stored) => stored
904 .downcast_ref::<T>()
905 .is_some_and(|stored| stored == scope),
906 ScopeStorage::RecomposeScope { .. } => false,
907 }
908 }
909
910 fn matches_predicate(&self, predicate: &impl Fn(&dyn Any) -> bool) -> bool {
911 match &self.scope {
912 ScopeStorage::Owned(scope) => predicate(scope.as_ref()),
913 ScopeStorage::RecomposeScope { weak, .. } => weak
914 .upgrade()
915 .is_none_or(|inner| predicate(&RecomposeScope { inner })),
916 }
917 }
918
919 fn holds_recompose_scope(&self) -> bool {
920 matches!(self.scope, ScopeStorage::RecomposeScope { .. })
921 }
922
923 fn live_recompose_scope(&self) -> Option<RecomposeScope> {
924 match &self.scope {
925 ScopeStorage::Owned(_) => None,
926 ScopeStorage::RecomposeScope { weak, .. } => {
927 weak.upgrade().map(|inner| RecomposeScope { inner })
928 }
929 }
930 }
931
932 fn notify(&self) {
933 match &self.scope {
934 ScopeStorage::Owned(scope) => (self.on_changed)(scope.as_ref()),
935 ScopeStorage::RecomposeScope { weak, .. } => {
936 if let Some(inner) = weak.upgrade() {
937 (self.on_changed)(&RecomposeScope { inner });
938 }
939 }
940 }
941 }
942}
943
944impl ScopeStorage {
945 fn from_value<T>(value: T) -> Self
946 where
947 T: Any + 'static,
948 {
949 let any = &value as &dyn Any;
950 if let Some(scope) = any.downcast_ref::<RecomposeScope>() {
951 Self::RecomposeScope {
952 id: scope.id(),
953 weak: scope.downgrade(),
954 }
955 } else {
956 Self::Owned(Box::new(value))
957 }
958 }
959}
960
961#[derive(Default)]
962pub(crate) struct ScopeObservers {
963 head: Cell<Option<Box<ObserverLink>>>,
964}
965
966struct ObserverLink {
967 observer: Weak<SnapshotStateObserverInner>,
968 entry: Rc<RefCell<ScopeEntry>>,
969 next: Option<Box<ObserverLink>>,
970}
971
972impl ScopeObservers {
973 fn find(&self, observer: &SnapshotStateObserverInner) -> Option<Rc<RefCell<ScopeEntry>>> {
974 let head = self.head.take();
975 let mut link = head.as_deref();
976 let mut found = None;
977 while let Some(current) = link {
978 if std::ptr::eq(current.observer.as_ptr(), observer) {
979 found = Some(Rc::clone(¤t.entry));
980 break;
981 }
982 link = current.next.as_deref();
983 }
984 self.head.set(head);
985 found
986 }
987
988 fn link(&self, observer: Weak<SnapshotStateObserverInner>, entry: Rc<RefCell<ScopeEntry>>) {
989 let next = self.head.take();
990 self.head.set(Some(Box::new(ObserverLink {
991 observer,
992 entry,
993 next,
994 })));
995 }
996
997 fn unlink(&self, observer: &SnapshotStateObserverInner) -> Option<Rc<RefCell<ScopeEntry>>> {
998 let mut rest = self.head.take();
999 let mut kept = None;
1000 let mut removed = None;
1001 while let Some(mut link) = rest {
1002 rest = link.next.take();
1003 if removed.is_none() && std::ptr::eq(link.observer.as_ptr(), observer) {
1004 removed = Some(link.entry);
1005 } else {
1006 link.next = kept;
1007 kept = Some(link);
1008 }
1009 }
1010 self.head.set(kept);
1011 removed
1012 }
1013
1014 pub(crate) fn release(&mut self) {
1015 let mut rest = self.head.get_mut().take();
1016 while let Some(link) = rest {
1017 let ObserverLink {
1018 observer,
1019 entry,
1020 next,
1021 } = *link;
1022 rest = next;
1023 if let Some(observer) = observer.upgrade() {
1024 observer.dead_entries.borrow_mut().push(entry);
1025 }
1026 }
1027 }
1028}
1029
1030impl Drop for SnapshotStateObserverInner {
1031 fn drop(&mut self) {
1032 for entry in self.indexed_scopes.borrow().values() {
1033 self.unlink_entry(entry);
1034 }
1035 }
1036}
1037
1038#[cfg(test)]
1039#[path = "tests/snapshot_state_observer_tests.rs"]
1040mod tests;