Skip to main content

ferroui_base/utilities/
weak_event.rs

1use super::{EventArgs, IWeakEventSubscriber, WeakHashList};
2use crate::threading::{Dispatcher, DispatcherPriority};
3use crate::{ObjectType, Ref, WeakRef};
4use std::any::Any;
5use std::cell::{Cell, RefCell};
6use std::collections::HashMap;
7use std::rc::{Rc, Weak};
8
9/// The arguments of an event that can be subscribed to weakly.
10///
11/// The arguments type of the managed event is a type parameter of
12/// [`WeakEvent`] and [`IWeakEventSubscriber`]. Event arguments are passed by
13/// reference and may themselves borrow data for the duration of the
14/// notification ([`FerroPropertyChangedEventArgs`](crate::FerroPropertyChangedEventArgs)),
15/// so the type parameter names the arguments type and `Args<'a>` is the
16/// type that is passed.
17pub trait WeakEventArgs: 'static {
18    type Args<'a>: ?Sized;
19}
20
21impl WeakEventArgs for EventArgs {
22    type Args<'a> = EventArgs;
23}
24
25/// Property change notifications of a model object: the name of the property.
26impl WeakEventArgs for str {
27    type Args<'a> = str;
28}
29
30/// A handle to an object that raises an event subscribed to through a
31/// [`WeakEvent`]: the managed reference type of the sender.
32///
33/// A [`WeakEvent`] identifies a sender by the address of the object the
34/// handle refers to and holds it weakly.
35pub trait WeakEventSender: Clone + 'static {
36    /// The weak form of the handle.
37    type Weak: Clone + 'static;
38
39    fn downgrade_sender(&self) -> Self::Weak;
40
41    fn upgrade_sender(weak: &Self::Weak) -> Option<Self>;
42
43    /// The address of the object the handle refers to.
44    fn sender_address(&self) -> usize;
45}
46
47impl<T: ?Sized + 'static> WeakEventSender for Rc<T> {
48    type Weak = Weak<T>;
49
50    fn downgrade_sender(&self) -> Weak<T> {
51        Rc::downgrade(self)
52    }
53
54    fn upgrade_sender(weak: &Weak<T>) -> Option<Self> {
55        weak.upgrade()
56    }
57
58    fn sender_address(&self) -> usize {
59        Rc::as_ptr(self) as *const () as usize
60    }
61}
62
63impl<T: ObjectType> WeakEventSender for Ref<T> {
64    type Weak = WeakRef<T>;
65
66    fn downgrade_sender(&self) -> WeakRef<T> {
67        self.downgrade()
68    }
69
70    fn upgrade_sender(weak: &WeakRef<T>) -> Option<Self> {
71        weak.upgrade()
72    }
73
74    fn sender_address(&self) -> usize {
75        &**self as *const T as *const () as usize
76    }
77}
78
79/// The signature of the handler a [`WeakEvent`] adds to the event of a
80/// sender: the managed `EventHandler<TEventArgs>` (sender and arguments).
81pub type WeakEventHandler<A> = dyn for<'s, 'r, 'a> Fn(Option<&'s dyn Any>, &'r <A as WeakEventArgs>::Args<'a>);
82
83/// The function a [`WeakEvent`] subscribes to the event of a sender with:
84/// it adds the handler and returns the action that removes it.
85type SubscribeFunc<S, A> = dyn Fn(&S, Rc<WeakEventHandler<A>>) -> Box<dyn FnOnce()>;
86
87/// Manages subscriptions to events using weak listeners.
88///
89/// One instance describes one event (usually a thread-local static, see
90/// [`WeakEvents`](super::WeakEvents)). For each sender it adds a single
91/// handler to the event, while there are subscribers, and forwards the
92/// events to the subscribers that are still alive. The sender holds the
93/// subscription; neither the sender nor the subscription holds the
94/// subscribers.
95pub struct WeakEvent<S: WeakEventSender, A: WeakEventArgs + ?Sized> {
96    this: Weak<WeakEvent<S, A>>,
97    subscribe: Box<SubscribeFunc<S, A>>,
98    subscriptions: RefCell<SubscriptionTable<S, A>>,
99}
100
101/// The subscriptions of a [`WeakEvent`] by sender address (the managed
102/// conditional weak table). A subscription lives as long as the handler it
103/// added to its sender; an entry whose subscription is gone is replaced, and
104/// such entries are swept when the table grows.
105struct SubscriptionTable<S: WeakEventSender, A: WeakEventArgs + ?Sized> {
106    entries: HashMap<usize, Weak<Subscription<S, A>>>,
107    sweep_at: usize,
108}
109
110const MIN_SWEEP: usize = 16;
111
112impl<S: WeakEventSender, A: WeakEventArgs + ?Sized> WeakEvent<S, A> {
113    fn new(subscribe: Box<SubscribeFunc<S, A>>) -> Rc<Self> {
114        Rc::new_cyclic(|this| Self {
115            this: this.clone(),
116            subscribe,
117            subscriptions: RefCell::new(SubscriptionTable { entries: HashMap::new(), sweep_at: MIN_SWEEP }),
118        })
119    }
120
121    /// Registers an event through the functions that add a handler to it
122    /// and remove the handler from it.
123    ///
124    /// The managed original removes the handler through the sender it
125    /// captured; here the removal holds the sender weakly (a strong
126    /// reference would keep it alive through its own subscription), so the
127    /// handler is not removed from a sender that no longer exists. It also
128    /// holds the handler weakly: the handler holds the subscription, which
129    /// holds the removal, and the sender owns the handler.
130    pub fn register<Sub, Unsub>(subscribe: Sub, unsubscribe: Unsub) -> Rc<Self>
131    where
132        Sub: Fn(&S, &Rc<WeakEventHandler<A>>) + 'static,
133        Unsub: Fn(&S, &Rc<WeakEventHandler<A>>) + 'static,
134    {
135        let unsubscribe = Rc::new(unsubscribe);
136        Self::new(Box::new(move |t: &S, s: Rc<WeakEventHandler<A>>| {
137            subscribe(t, &s);
138            let t = t.downgrade_sender();
139            let s = Rc::downgrade(&s);
140            let unsubscribe = unsubscribe.clone();
141            Box::new(move || {
142                if let (Some(t), Some(s)) = (S::upgrade_sender(&t), s.upgrade()) {
143                    unsubscribe(&t, &s);
144                }
145            })
146        }))
147    }
148
149    /// Registers an event through a function that adds a handler to it and
150    /// returns the action that removes the handler.
151    ///
152    /// The action must not hold the sender strongly: the sender holds the
153    /// subscription, and the subscription holds the action.
154    pub fn register_returning_unsubscribe<Sub>(subscribe: Sub) -> Rc<Self>
155    where
156        Sub: Fn(&S, Rc<WeakEventHandler<A>>) -> Box<dyn FnOnce()> + 'static,
157    {
158        Self::new(Box::new(subscribe))
159    }
160
161    pub fn subscribe(&self, target: &S, subscriber: &Rc<dyn IWeakEventSubscriber<A>>) {
162        loop {
163            let subscription = self.get_value(target);
164            if subscription.add(subscriber) {
165                break;
166            }
167        }
168    }
169
170    pub fn unsubscribe(&self, target: &S, subscriber: &Rc<dyn IWeakEventSubscriber<A>>) {
171        if let Some(subscription) = self.try_get_value(target) {
172            subscription.remove(subscriber);
173        }
174    }
175
176    fn try_get_value(&self, target: &S) -> Option<Rc<Subscription<S, A>>> {
177        self.subscriptions.borrow().entries.get(&target.sender_address()).and_then(Weak::upgrade)
178    }
179
180    fn get_value(&self, target: &S) -> Rc<Subscription<S, A>> {
181        if let Some(subscription) = self.try_get_value(target) {
182            return subscription;
183        }
184        let subscription = self.create_subscription(target);
185        let mut table = self.subscriptions.borrow_mut();
186        table.entries.insert(subscription.address, Rc::downgrade(&subscription));
187        if table.entries.len() >= table.sweep_at {
188            table.entries.retain(|_, subscription| subscription.strong_count() > 0);
189            table.sweep_at = (table.entries.len() * 2).max(MIN_SWEEP);
190        }
191        subscription
192    }
193
194    fn create_subscription(&self, key: &S) -> Rc<Subscription<S, A>> {
195        Subscription::new(self.this.upgrade().expect("a weak event is used through its handle"), key)
196    }
197}
198
199impl<S: WeakEventSender> WeakEvent<S, EventArgs> {
200    /// Registers an event whose handlers take the plain event arguments,
201    /// through the functions that add a handler to it and remove the
202    /// handler from it.
203    pub fn register_plain<Sub, Unsub>(subscribe: Sub, unsubscribe: Unsub) -> Rc<Self>
204    where
205        Sub: Fn(&S, &Rc<WeakEventHandler<EventArgs>>) + 'static,
206        Unsub: Fn(&S, &Rc<WeakEventHandler<EventArgs>>) + 'static,
207    {
208        let unsubscribe = Rc::new(unsubscribe);
209        Self::register_returning_unsubscribe(move |s: &S, h: Rc<WeakEventHandler<EventArgs>>| {
210            // The handler belongs to the sender, so it refers to it weakly.
211            let sender = s.downgrade_sender();
212            let handler: Rc<WeakEventHandler<EventArgs>> = Rc::new(move |_: Option<&dyn Any>, e: &EventArgs| {
213                if let Some(s) = S::upgrade_sender(&sender) {
214                    h(Some(&s as &dyn Any), e);
215                }
216            });
217            subscribe(s, &handler);
218            // The sender owns the handler, which holds the subscription: the
219            // removal refers to both weakly.
220            let s = s.downgrade_sender();
221            let handler = Rc::downgrade(&handler);
222            let unsubscribe = unsubscribe.clone();
223            Box::new(move || {
224                if let (Some(s), Some(handler)) = (S::upgrade_sender(&s), handler.upgrade()) {
225                    unsubscribe(&s, &handler);
226                }
227            })
228        })
229    }
230}
231
232/// The subscription of a [`WeakEvent`] to the event of one sender.
233struct Subscription<S: WeakEventSender, A: WeakEventArgs + ?Sized> {
234    this: Weak<Subscription<S, A>>,
235    ev: Rc<WeakEvent<S, A>>,
236    target: S::Weak,
237    address: usize,
238    list: RefCell<WeakHashList<dyn IWeakEventSubscriber<A>>>,
239    unsubscribe: RefCell<Option<Box<dyn FnOnce()>>>,
240    compact_scheduled: Cell<bool>,
241    destroyed: Cell<bool>,
242}
243
244impl<S: WeakEventSender, A: WeakEventArgs + ?Sized> Subscription<S, A> {
245    fn new(ev: Rc<WeakEvent<S, A>>, target: &S) -> Rc<Self> {
246        Rc::new_cyclic(|this| Self {
247            this: this.clone(),
248            ev,
249            target: target.downgrade_sender(),
250            address: target.sender_address(),
251            list: RefCell::new(WeakHashList::new()),
252            unsubscribe: RefCell::new(None),
253            compact_scheduled: Cell::new(false),
254            destroyed: Cell::new(false),
255        })
256    }
257
258    fn destroy(&self) {
259        if self.destroyed.get() {
260            return;
261        }
262        self.destroyed.set(true);
263        let unsubscribe = self.unsubscribe.borrow_mut().take();
264        if let Some(unsubscribe) = unsubscribe {
265            unsubscribe();
266        }
267        let mut table = self.ev.subscriptions.borrow_mut();
268        if table.entries.get(&self.address).is_some_and(|entry| std::ptr::eq(entry.as_ptr(), self)) {
269            table.entries.remove(&self.address);
270        }
271    }
272
273    fn add(&self, s: &Rc<dyn IWeakEventSubscriber<A>>) -> bool {
274        if self.destroyed.get() {
275            return false;
276        }
277
278        if self.unsubscribe.borrow().is_none() {
279            let Some(target) = S::upgrade_sender(&self.target) else { return false };
280            // The handler is held by the sender and holds the subscription.
281            let this = self.this.upgrade().expect("a subscription is used through its handle");
282            let handler: Rc<WeakEventHandler<A>> =
283                Rc::new(move |sender: Option<&dyn Any>, event_args: &A::Args<'_>| this.on_event(sender, event_args));
284            let unsubscribe = (self.ev.subscribe)(&target, handler);
285            *self.unsubscribe.borrow_mut() = Some(unsubscribe);
286        }
287        self.list.borrow_mut().add(s);
288        true
289    }
290
291    fn remove(&self, s: &Rc<dyn IWeakEventSubscriber<A>>) {
292        if self.destroyed.get() {
293            return;
294        }
295
296        let mut list = self.list.borrow_mut();
297        list.remove(s);
298        if list.is_empty() {
299            drop(list);
300            self.destroy();
301        } else if list.need_compact() && self.compact_scheduled.get() {
302            drop(list);
303            self.schedule_compact();
304        }
305    }
306
307    fn schedule_compact(&self) {
308        if self.compact_scheduled.get() || self.destroyed.get() {
309            return;
310        }
311        self.compact_scheduled.set(true);
312        let Some(this) = self.this.upgrade() else { return };
313        // The sender, and so the subscription, belongs to this thread.
314        Dispatcher::current_dispatcher().post_local(move || this.compact(), DispatcherPriority::BACKGROUND);
315    }
316
317    fn compact(&self) {
318        if self.destroyed.get() {
319            return;
320        }
321        if !self.compact_scheduled.get() {
322            return;
323        }
324        self.compact_scheduled.set(false);
325        let mut list = self.list.borrow_mut();
326        list.compact();
327        if list.is_empty() {
328            drop(list);
329            self.destroy();
330        }
331    }
332
333    fn on_event(&self, _sender: Option<&dyn Any>, event_args: &A::Args<'_>) {
334        let _keep_alive = self.this.upgrade();
335        let alive = self.list.borrow_mut().get_alive(None);
336        let Some(alive) = alive else {
337            self.destroy();
338            return;
339        };
340
341        let target = S::upgrade_sender(&self.target);
342        let sender = target.as_ref().map(|target| target as &dyn Any);
343        let ev: &dyn Any = &*self.ev;
344        for item in &alive {
345            item.on_event(sender, ev, event_args);
346        }
347
348        WeakHashList::<dyn IWeakEventSubscriber<A>>::return_to_shared_pool(alive);
349        if self.list.borrow().need_compact() && !self.compact_scheduled.get() {
350            self.schedule_compact();
351        }
352    }
353}
354
355#[cfg(test)]
356mod tests {
357    use super::*;
358
359    struct EventSource {
360        this: Weak<EventSource>,
361        event: RefCell<Vec<Rc<WeakEventHandler<EventArgs>>>>,
362    }
363
364    impl EventSource {
365        fn new() -> Rc<Self> {
366            Rc::new_cyclic(|this| Self { this: this.clone(), event: RefCell::new(Vec::new()) })
367        }
368
369        fn add_event(&self, handler: &Rc<WeakEventHandler<EventArgs>>) {
370            self.event.borrow_mut().push(handler.clone());
371        }
372
373        fn remove_event(&self, handler: &Rc<WeakEventHandler<EventArgs>>) {
374            let mut event = self.event.borrow_mut();
375            if let Some(index) = event.iter().rposition(|h| Rc::ptr_eq(h, handler)) {
376                event.remove(index);
377            }
378        }
379
380        fn fire(&self) {
381            let this = self.this.upgrade();
382            let handlers = self.event.borrow().clone();
383            for handler in handlers {
384                handler(this.as_ref().map(|this| this as &dyn Any), &EventArgs::EMPTY);
385            }
386        }
387
388        fn weak_ev() -> Rc<WeakEvent<Rc<EventSource>, EventArgs>> {
389            crate::ferro_thread_local! {
390                static WEAK_EV: Rc<WeakEvent<Rc<EventSource>, EventArgs>> = WeakEvent::register_plain(
391                    |t: &Rc<EventSource>, s| t.add_event(s),
392                    |t: &Rc<EventSource>, s| t.remove_event(s));
393            }
394            WEAK_EV.with(Rc::clone)
395        }
396    }
397
398    struct Subscriber {
399        on_event: Option<Box<dyn Fn()>>,
400    }
401
402    impl Subscriber {
403        fn new(on_event: Option<Box<dyn Fn()>>) -> Rc<dyn IWeakEventSubscriber<EventArgs>> {
404            Rc::new(Self { on_event })
405        }
406    }
407
408    impl IWeakEventSubscriber<EventArgs> for Subscriber {
409        fn on_event(&self, _sender: Option<&dyn Any>, _ev: &dyn Any, _args: &EventArgs) {
410            if let Some(on_event) = &self.on_event {
411                on_event();
412            }
413        }
414    }
415
416    #[test]
417    fn event_should_be_passed_to_subscriber() {
418        let handled = Rc::new(Cell::new(false));
419        let h = handled.clone();
420        let subscriber = Subscriber::new(Some(Box::new(move || h.set(true))));
421        let source = EventSource::new();
422        EventSource::weak_ev().subscribe(&source, &subscriber);
423
424        source.fire();
425        assert!(handled.get());
426    }
427
428    #[test]
429    fn event_handler_should_not_be_kept_alive() {
430        let handled = Rc::new(Cell::new(false));
431        let source = EventSource::new();
432        let h = handled.clone();
433        add_subscriber(&source, Box::new(move || h.set(true)));
434        source.fire();
435        assert!(!handled.get());
436    }
437
438    /// Not from upstream: the managed test relies on the garbage collector;
439    /// here dropping the source must free the subscription and the source.
440    #[test]
441    fn dropping_the_source_frees_the_subscription() {
442        let subscriber = Subscriber::new(None);
443        let source = EventSource::new();
444        let weak_ev = EventSource::weak_ev();
445        weak_ev.subscribe(&source, &subscriber);
446        let subscription = weak_ev.subscriptions.borrow().entries.get(&source.sender_address()).cloned().unwrap();
447        assert!(subscription.upgrade().is_some());
448
449        drop(source);
450
451        // The subscription, and with it its weak reference to the source, is gone.
452        assert!(subscription.upgrade().is_none());
453    }
454
455    fn add_subscriber(source: &Rc<EventSource>, func: Box<dyn Fn()>) {
456        EventSource::weak_ev().subscribe(source, &Subscriber::new(Some(func)));
457    }
458}