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
9pub trait WeakEventArgs: 'static {
18 type Args<'a>: ?Sized;
19}
20
21impl WeakEventArgs for EventArgs {
22 type Args<'a> = EventArgs;
23}
24
25impl WeakEventArgs for str {
27 type Args<'a> = str;
28}
29
30pub trait WeakEventSender: Clone + 'static {
36 type Weak: Clone + 'static;
38
39 fn downgrade_sender(&self) -> Self::Weak;
40
41 fn upgrade_sender(weak: &Self::Weak) -> Option<Self>;
42
43 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
79pub type WeakEventHandler<A> = dyn for<'s, 'r, 'a> Fn(Option<&'s dyn Any>, &'r <A as WeakEventArgs>::Args<'a>);
82
83type SubscribeFunc<S, A> = dyn Fn(&S, Rc<WeakEventHandler<A>>) -> Box<dyn FnOnce()>;
86
87pub 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
101struct 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 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 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 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 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 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
232struct 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 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 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 #[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 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}