diff --git a/litebox/src/event/observer.rs b/litebox/src/event/observer.rs index 4e42a304e..42cdbd409 100644 --- a/litebox/src/event/observer.rs +++ b/litebox/src/event/observer.rs @@ -93,9 +93,28 @@ impl, Platform: RawSyncPrimitivesProvider> Subject, F>) { + observers.retain(|observer, _| { + if observer.upgrade().is_some() { + true + } else { + self.nums.fetch_sub(1, Ordering::Relaxed); + false + } + }); + } + /// Register an observer with the given filter. pub fn register_observer(&self, observer: Weak>, filter: F) { let mut observers = self.observers.lock(); + self.prune_dead_observers(&mut observers); if observers .insert(ObserverKey::new(observer), filter) .is_none() @@ -119,16 +138,65 @@ impl, Platform: RawSyncPrimitivesProvider> Subject for TestObserver { + fn on_events(&self, _events: &Events) { + self.notifications.fetch_add(1, Ordering::Relaxed); + } + } + + #[test] + fn register_observer_prunes_dead_entries() { + let subject = Subject::::new(); + + let stale = Arc::new(TestObserver { + notifications: AtomicUsize::new(0), + }); + subject.register_observer(Arc::downgrade(&stale) as _, Events::IN); + assert_eq!(subject.nums.load(Ordering::Relaxed), 1); + assert_eq!(subject.observers.lock().len(), 1); + drop(stale); + + let fresh = Arc::new(TestObserver { + notifications: AtomicUsize::new(0), }); + subject.register_observer(Arc::downgrade(&fresh) as _, Events::OUT); + { + let observers = subject.observers.lock(); + let registered = observers + .keys() + .next() + .and_then(super::ObserverKey::upgrade) + .expect("dead observer should be pruned during registration"); + let fresh_observer: Arc> = fresh.clone(); + assert!(Arc::ptr_eq(®istered, &fresh_observer)); + assert_eq!(subject.nums.load(Ordering::Relaxed), 1); + assert_eq!(observers.len(), 1); + } + subject.notify_observers(Events::OUT); + + assert_eq!(fresh.notifications.load(Ordering::Relaxed), 1); } }