Skip to main content

ferroui_base/utilities/
weak_event_handler_manager.rs

1use super::{WeakEventArgs, WeakEventHandler, WeakEventSender};
2use std::any::{Any, TypeId};
3use std::cell::{Cell, RefCell};
4use std::collections::HashMap;
5use std::ops::Deref;
6use std::rc::{Rc, Weak};
7
8/// The events of a type, found by name: what the managed original looks up
9/// through reflection on the target type.
10pub trait WeakEventHandlerTarget<A: WeakEventArgs + ?Sized> {
11    /// Adds `handler` to the event named `event_name` and returns the action
12    /// that removes it, or `None` if the type has no such event.
13    ///
14    /// The action must not hold the target strongly: the target holds the
15    /// subscription through the handler, and the subscription holds the
16    /// action.
17    fn add_event_handler(&self, event_name: &str, handler: Rc<WeakEventHandler<A>>) -> Option<Box<dyn FnOnce()>>;
18}
19
20/// The method a subscription calls on its subscriber: the method of the
21/// managed delegate, whose target is the subscriber.
22pub type WeakEventHandlerMethod<A, TSubscriber> =
23    for<'s, 'r, 'a> fn(&TSubscriber, Option<&'s dyn Any>, &'r <A as WeakEventArgs>::Args<'a>);
24
25/// Manages subscriptions to events using weak listeners.
26pub struct WeakEventHandlerManager;
27
28impl WeakEventHandlerManager {
29    /// Subscribes to an event on an object using a weak subscription.
30    ///
31    /// The managed delegate is given as its two parts: the subscriber (its
32    /// target), which is referenced weakly, and the method called on it.
33    ///
34    /// # Panics
35    /// Panics if the type of `target` has no event named `event_name`.
36    pub fn subscribe<TTarget, A, TSubscriber>(
37        target: &TTarget,
38        event_name: &str,
39        subscriber: &Rc<TSubscriber>,
40        method: WeakEventHandlerMethod<A, TSubscriber>,
41    ) where
42        TTarget: WeakEventSender + Deref,
43        TTarget::Target: WeakEventHandlerTarget<A>,
44        A: WeakEventArgs + ?Sized,
45        TSubscriber: 'static,
46    {
47        let dic = SubscriptionTypeStorage::<A, TSubscriber>::subscribers().get_or_create_value(target);
48
49        let existing = dic.subscriptions.borrow().get(event_name).cloned();
50        let sub = match existing {
51            Some(sub) => sub,
52            None => {
53                let sub = Subscription::new(&dic, target, event_name);
54                dic.subscriptions.borrow_mut().insert(event_name.to_string(), sub.clone());
55                sub
56            }
57        };
58
59        sub.add(subscriber, method);
60    }
61
62    /// Unsubscribes from an event.
63    ///
64    /// Every subscription of `subscriber` to the event is removed, whatever
65    /// method it calls, as the managed original compares the delegate
66    /// targets only.
67    pub fn unsubscribe<A, TSubscriber>(target: &impl WeakEventSender, event_name: &str, subscriber: &Rc<TSubscriber>)
68    where
69        A: WeakEventArgs + ?Sized,
70        TSubscriber: 'static,
71    {
72        if let Some(dic) = SubscriptionTypeStorage::<A, TSubscriber>::subscribers().try_get_value(target) {
73            let sub = dic.subscriptions.borrow().get(event_name).cloned();
74            if let Some(sub) = sub {
75                sub.remove(subscriber);
76            }
77        }
78    }
79}
80
81crate::ferro_thread_local! {
82    /// The storage of each pair of event arguments and subscriber types.
83    static SUBSCRIPTION_TYPE_STORAGE: RefCell<HashMap<TypeId, Box<dyn Any>>> = RefCell::new(HashMap::new());
84}
85
86/// The subscriptions of one pair of event arguments and subscriber types, by
87/// target address (the managed conditional weak table). An entry keeps the
88/// address of its target from being reused; entries of targets that are
89/// gone are swept when the table grows.
90struct SubscriptionTypeStorage<A: WeakEventArgs + ?Sized, TSubscriber: 'static> {
91    subscribers: RefCell<HashMap<usize, Rc<SubscriptionDic<A, TSubscriber>>>>,
92    sweep_at: Cell<usize>,
93}
94
95const MIN_SWEEP: usize = 16;
96
97impl<A: WeakEventArgs + ?Sized, TSubscriber: 'static> SubscriptionTypeStorage<A, TSubscriber> {
98    fn subscribers() -> Rc<Self> {
99        SUBSCRIPTION_TYPE_STORAGE.with(|storage| {
100            storage
101                .borrow_mut()
102                .entry(TypeId::of::<fn(&A) -> TSubscriber>())
103                .or_insert_with(|| {
104                    Box::new(Rc::new(Self { subscribers: RefCell::new(HashMap::new()), sweep_at: Cell::new(MIN_SWEEP) }))
105                })
106                .downcast_ref::<Rc<Self>>()
107                .expect("the storage of a type pair holds that pair")
108                .clone()
109        })
110    }
111
112    fn try_get_value(&self, target: &impl WeakEventSender) -> Option<Rc<SubscriptionDic<A, TSubscriber>>> {
113        self.subscribers.borrow().get(&target.sender_address()).cloned()
114    }
115
116    fn get_or_create_value<TTarget: WeakEventSender>(&self, target: &TTarget) -> Rc<SubscriptionDic<A, TSubscriber>> {
117        if let Some(dic) = self.try_get_value(target) {
118            return dic;
119        }
120        let weak_target = target.downgrade_sender();
121        let dic = Rc::new(SubscriptionDic {
122            subscriptions: RefCell::new(HashMap::new()),
123            target_alive: Box::new(move || TTarget::upgrade_sender(&weak_target).is_some()),
124        });
125        let mut subscribers = self.subscribers.borrow_mut();
126        subscribers.insert(target.sender_address(), dic.clone());
127        if subscribers.len() >= self.sweep_at.get() {
128            subscribers.retain(|_, dic| (dic.target_alive)());
129            self.sweep_at.set((subscribers.len() * 2).max(MIN_SWEEP));
130        }
131        dic
132    }
133}
134
135/// The subscriptions to the events of one target, by event name.
136struct SubscriptionDic<A: WeakEventArgs + ?Sized, TSubscriber: 'static> {
137    subscriptions: RefCell<HashMap<String, Rc<Subscription<A, TSubscriber>>>>,
138    target_alive: Box<dyn Fn() -> bool>,
139}
140
141struct Descriptor<A: WeakEventArgs + ?Sized, TSubscriber> {
142    subscriber: Option<Weak<TSubscriber>>,
143    caller: Option<WeakEventHandlerMethod<A, TSubscriber>>,
144}
145
146impl<A: WeakEventArgs + ?Sized, TSubscriber> Default for Descriptor<A, TSubscriber> {
147    fn default() -> Self {
148        Self { subscriber: None, caller: None }
149    }
150}
151
152impl<A: WeakEventArgs + ?Sized, TSubscriber> Clone for Descriptor<A, TSubscriber> {
153    fn clone(&self) -> Self {
154        Self { subscriber: self.subscriber.clone(), caller: self.caller }
155    }
156}
157
158struct Subscription<A: WeakEventArgs + ?Sized, TSubscriber: 'static> {
159    sdic: Weak<SubscriptionDic<A, TSubscriber>>,
160    event_name: String,
161    remove_handler: RefCell<Option<Box<dyn FnOnce()>>>,
162    data: RefCell<Vec<Descriptor<A, TSubscriber>>>,
163    count: Cell<usize>,
164}
165
166impl<A: WeakEventArgs + ?Sized, TSubscriber: 'static> Subscription<A, TSubscriber> {
167    fn new<TTarget>(sdic: &Rc<SubscriptionDic<A, TSubscriber>>, target: &TTarget, event_name: &str) -> Rc<Self>
168    where
169        TTarget: WeakEventSender + Deref,
170        TTarget::Target: WeakEventHandlerTarget<A>,
171    {
172        let this = Rc::new(Self {
173            sdic: Rc::downgrade(sdic),
174            event_name: event_name.to_string(),
175            remove_handler: RefCell::new(None),
176            data: RefCell::new(vec![Descriptor::default(); 2]),
177            count: Cell::new(0),
178        });
179
180        // The target holds the handler; the table holds the subscription.
181        let weak = Rc::downgrade(&this);
182        let delegate: Rc<WeakEventHandler<A>> = Rc::new(move |sender: Option<&dyn Any>, event_args: &A::Args<'_>| {
183            if let Some(this) = weak.upgrade() {
184                this.on_event(sender, event_args);
185            }
186        });
187        match (**target).add_event_handler(event_name, delegate) {
188            Some(remove_handler) => *this.remove_handler.borrow_mut() = Some(remove_handler),
189            None => panic!("The event {event_name} was not found on {}.", std::any::type_name::<TTarget::Target>()),
190        }
191        this
192    }
193
194    fn destroy(&self) {
195        let remove_handler = self.remove_handler.borrow_mut().take();
196        if let Some(remove_handler) = remove_handler {
197            remove_handler();
198        }
199        if let Some(sdic) = self.sdic.upgrade() {
200            sdic.subscriptions.borrow_mut().remove(&self.event_name);
201        }
202    }
203
204    fn add(&self, subscriber: &Rc<TSubscriber>, method: WeakEventHandlerMethod<A, TSubscriber>) {
205        self.compact(true);
206        let mut data = self.data.borrow_mut();
207        if self.count.get() == data.len() {
208            // Extend capacity
209            let len = data.len();
210            data.resize(len * 2, Descriptor::default());
211        }
212
213        data[self.count.get()] = Descriptor { caller: Some(method), subscriber: Some(Rc::downgrade(subscriber)) };
214        self.count.set(self.count.get() + 1);
215    }
216
217    fn remove(&self, subscriber: &Rc<TSubscriber>) {
218        let mut removed = false;
219
220        {
221            let mut data = self.data.borrow_mut();
222            for c in 0..self.count.get() {
223                let is_subscriber = data[c]
224                    .subscriber
225                    .as_ref()
226                    .and_then(Weak::upgrade)
227                    .is_some_and(|instance| Rc::ptr_eq(&instance, subscriber));
228                if is_subscriber {
229                    data[c] = Descriptor::default();
230                    removed = true;
231                }
232            }
233        }
234
235        if removed {
236            self.compact(false);
237        }
238    }
239
240    fn compact(&self, prevent_destroy: bool) {
241        let mut empty: Option<usize> = None;
242        {
243            let mut data = self.data.borrow_mut();
244            for c in 0..self.count.get() {
245                let r = data[c].clone();
246
247                let target = r.subscriber.as_ref().and_then(Weak::upgrade);
248
249                // Mark current index as first empty
250                if target.is_none() && empty.is_none() {
251                    empty = Some(c);
252                }
253                // If current element isn't null and we have an empty one
254                if let (Some(_), Some(e)) = (&target, empty) {
255                    data[c] = Descriptor::default();
256                    data[e] = r;
257                    empty = Some(e + 1);
258                }
259            }
260        }
261        if let Some(empty) = empty {
262            self.count.set(empty);
263        }
264        if self.count.get() == 0 && !prevent_destroy {
265            self.destroy();
266        }
267    }
268
269    fn on_event(&self, sender: Option<&dyn Any>, event_args: &A::Args<'_>) {
270        let mut need_compact = false;
271        let mut c = 0;
272        while c < self.count.get() {
273            let descriptor = self.data.borrow()[c].clone();
274            let r = descriptor.subscriber.expect("a counted descriptor has a subscriber");
275            match r.upgrade() {
276                Some(sub) => (descriptor.caller.expect("a counted descriptor has a caller"))(&sub, sender, event_args),
277                None => need_compact = true,
278            }
279            c += 1;
280        }
281        if need_compact {
282            self.compact(false);
283        }
284    }
285}
286
287#[cfg(test)]
288mod tests {
289    use super::*;
290    use crate::utilities::EventArgs;
291
292    struct EventSource {
293        event: RefCell<Vec<Rc<WeakEventHandler<EventArgs>>>>,
294        this: Weak<EventSource>,
295    }
296
297    impl EventSource {
298        fn new() -> Rc<Self> {
299            Rc::new_cyclic(|this| Self { event: RefCell::new(Vec::new()), this: this.clone() })
300        }
301
302        fn fire(&self) {
303            let this = self.this.upgrade();
304            let handlers = self.event.borrow().clone();
305            for handler in handlers {
306                handler(this.as_ref().map(|this| this as &dyn Any), &EventArgs::EMPTY);
307            }
308        }
309    }
310
311    impl WeakEventHandlerTarget<EventArgs> for EventSource {
312        fn add_event_handler(
313            &self,
314            event_name: &str,
315            handler: Rc<WeakEventHandler<EventArgs>>,
316        ) -> Option<Box<dyn FnOnce()>> {
317            if event_name != "Event" {
318                return None;
319            }
320            self.event.borrow_mut().push(handler.clone());
321            let this = self.this.clone();
322            Some(Box::new(move || {
323                if let Some(this) = this.upgrade() {
324                    let mut event = this.event.borrow_mut();
325                    if let Some(index) = event.iter().rposition(|h| Rc::ptr_eq(h, &handler)) {
326                        event.remove(index);
327                    }
328                }
329            }))
330        }
331    }
332
333    struct Subscriber {
334        on_event: Option<Box<dyn Fn()>>,
335    }
336
337    impl Subscriber {
338        fn new(on_event: Box<dyn Fn()>) -> Rc<Self> {
339            Rc::new(Self { on_event: Some(on_event) })
340        }
341
342        fn on_event(&self, _sender: Option<&dyn Any>, _ev: &EventArgs) {
343            if let Some(on_event) = &self.on_event {
344                on_event();
345            }
346        }
347    }
348
349    #[test]
350    fn event_should_be_passed_to_subscriber() {
351        let handled = Rc::new(Cell::new(false));
352        let h = handled.clone();
353        let subscriber = Subscriber::new(Box::new(move || h.set(true)));
354        let source = EventSource::new();
355        WeakEventHandlerManager::subscribe::<_, EventArgs, Subscriber>(&source, "Event", &subscriber, Subscriber::on_event);
356        source.fire();
357        assert!(handled.get());
358    }
359
360    #[test]
361    fn event_should_not_be_raised_after_unsubscribe() {
362        let handled = Rc::new(Cell::new(false));
363        let h = handled.clone();
364        let subscriber = Subscriber::new(Box::new(move || h.set(true)));
365        let source = EventSource::new();
366        WeakEventHandlerManager::subscribe::<_, EventArgs, Subscriber>(&source, "Event", &subscriber, Subscriber::on_event);
367
368        WeakEventHandlerManager::unsubscribe::<EventArgs, Subscriber>(&source, "Event", &subscriber);
369
370        source.fire();
371
372        assert!(!handled.get());
373    }
374
375    #[test]
376    fn event_handler_should_not_be_kept_alive() {
377        let handled = Rc::new(Cell::new(false));
378        let source = EventSource::new();
379        let h = handled.clone();
380        add_collectable_subscriber(&source, "Event", Box::new(move || h.set(true)));
381        source.fire();
382        assert!(!handled.get());
383    }
384
385    fn add_collectable_subscriber(source: &Rc<EventSource>, name: &str, func: Box<dyn Fn()>) {
386        WeakEventHandlerManager::subscribe::<_, EventArgs, Subscriber>(source, name, &Subscriber::new(func), Subscriber::on_event);
387    }
388}