chore: refresco desde el monorepo (pin tawasuyu.git v0.2.0)
1//! A general purpose signaling mechanism
2//!
3//! This mechanism allows inter-module communication, by letting your modules
4//! register callbacks to listen for events generated by other modules. This
5//! signaling mechanism is synchronous and non-thread safe. If you need
6//! asynchronous thread safe communication, instead consider relying on channels.
7//!
8//! The whole mechanism is built on the [`Signaler`] type.
9//! It serves both as a message sending facility and a way to register new callbacks
10//! for these messages. It can be cloned and passed around between your modules with
11//! `Rc`-like semantics.
12//!
13//! When sending a new signal with [`Signaler::signal`], the provided value `E` will
14//! be made accessible as a reference `&E` to all registered callback.
15//!
16//! Sending a signal or registering a new callback from within a callback is supported.
17//! These will however take effect after the current signal is completely delivered.
18//! Ordering of sent signals and callback registration is preserved.
19
20use std::{
21 any::Any,
22 cell::RefCell,
23 collections::VecDeque,
24 fmt,
25 rc::{Rc, Weak},
26};
27
28/// A signaler, main type for signaling
29#[derive(Debug)]
30pub struct Signaler<S> {
31 inner: Rc<SignalInner<S>>,
32}
33
34// Manual clone impl because of type parameters
35impl<S> Clone for Signaler<S> {
36 fn clone(&self) -> Signaler<S> {
37 Signaler {
38 inner: self.inner.clone(),
39 }
40 }
41}
42
43impl<S> Signaler<S> {
44 /// Create a new signaler for given signal type
45 pub fn new() -> Signaler<S> {
46 Signaler {
47 inner: Rc::new(SignalInner::new()),
48 }
49 }
50
51 /// Register a new callback to this signaler
52 ///
53 /// This method returns a `SignalToken`, which you must keep as long
54 /// as you need your callback to remain in place. Dropping it will
55 /// disable and free your callback. If you don't plan to ever disable
56 /// your callback, see [`SignalToken::leak()`](./struct.SignalToken.html).
57 ///
58 /// If you register a callback from within a callback of the same Signaler,
59 /// the new callback will only be inserted *after* the current signal is
60 /// completely delivered, and thus will not receive it.
61 #[must_use]
62 pub fn register<F: FnMut(&S) + 'static>(&self, f: F) -> SignalToken {
63 let rc = Rc::new(RefCell::new(f));
64 let weak = Rc::downgrade(&rc) as Weak<RefCell<dyn FnMut(&S)>>;
65 self.inner.insert(weak);
66 SignalToken { signal: rc }
67 }
68
69 /// Signal the callbacks
70 ///
71 /// All registered callbacks will be invoked with a reference to the value
72 /// you provide here, after which that value will be dropped.
73 ///
74 /// If this method is invoked from within a callback of the same Signaler,
75 /// its signalling will be delayed until the current signal is completely
76 /// delivered and this method will return immediately.
77 pub fn signal(&self, signal: S) {
78 self.inner.send(signal);
79 }
80}
81
82impl<S> Default for Signaler<S> {
83 fn default() -> Self {
84 Self::new()
85 }
86}
87
88/// A token associated with a callback registered to a Signaler
89///
90/// Dropping it will disable and drop the callback it is associated to.
91/// If you don't plan to ever disable the callback, you can use the `leak`
92/// method to safely get rid of this value.
93#[derive(Debug)]
94pub struct SignalToken {
95 signal: Rc<dyn Any>,
96}
97
98impl SignalToken {
99 /// Destroy the token without disabling the associated callback
100 pub fn leak(self) {
101 // leak the Rc, so that it is never deallocated
102 let _ = Rc::into_raw(self.signal);
103 }
104}
105
106type WeakCallback<S> = Weak<RefCell<dyn FnMut(&S)>>;
107
108struct SignalInner<S> {
109 callbacks: RefCell<Vec<WeakCallback<S>>>,
110 pending_callbacks: RefCell<Vec<WeakCallback<S>>>,
111 pending_events: RefCell<VecDeque<S>>,
112}
113
114// WeakCallback does not implement debug, so we have to impl Debug manually
115impl<S: fmt::Debug> fmt::Debug for SignalInner<S> {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 f.debug_struct("SignalInner")
118 .field("callbacks::len()", &self.callbacks.borrow().len())
119 .field("pending_callbacks::len()", &self.pending_callbacks.borrow().len())
120 .field("pending_events", &self.pending_events)
121 .finish()
122 }
123}
124
125impl<S> SignalInner<S> {
126 fn new() -> SignalInner<S> {
127 SignalInner {
128 callbacks: RefCell::new(Vec::new()),
129 pending_callbacks: RefCell::new(Vec::new()),
130 pending_events: RefCell::new(VecDeque::new()),
131 }
132 }
133
134 fn insert(&self, weak: WeakCallback<S>) {
135 // attempt to insert the new callback
136 if let Ok(mut guard) = self.callbacks.try_borrow_mut() {
137 // success, insert it
138 guard.push(weak);
139 } else {
140 // The callback list is already borrowed, this means that this insertion is
141 // done from within a callback.
142 // In that case, insert the callback into the pending list, `send`
143 // will insert it in the callback list when it is finished dispatching
144 // the current event.
145 self.pending_callbacks.borrow_mut().push(weak);
146 }
147 }
148
149 fn send(&self, event: S) {
150 // insert the new event into the pending list
151 self.pending_events.borrow_mut().push_back(event);
152 // now try to dispatch the events from the pending list
153 // new events might be added by other callbacks in the process
154 // so we try to completely drain it before returning
155 //
156 // If we cannot get the guard, that means an other dispatching is
157 // already in progress. It'll empty the pending list, so there is
158 // nothing more we need to do.
159 if let Ok(mut guard) = self.callbacks.try_borrow_mut() {
160 // We cannot just use `while let` because this would keep the
161 // borrow of self.pending_events alive during the whole loop, rather
162 // than just the evaluation of the condition. :/
163 loop {
164 let next_event = self.pending_events.borrow_mut().pop_front();
165 if let Some(event) = next_event {
166 // Send the message, cleaning up defunct callbacks in the process
167 guard.retain(|weak| {
168 if let Some(cb) = Weak::upgrade(weak) {
169 (*cb.borrow_mut())(&event);
170 true
171 } else {
172 false
173 }
174 });
175 // integrate any pending callbacks resulting from the dispatching
176 // of this event
177 guard.extend(self.pending_callbacks.borrow_mut().drain(..));
178 } else {
179 break;
180 }
181 }
182 }
183 }
184}
185
186/// Trait representing the capability of an object to listen for some signals
187///
188/// It is provided so that the signaling system can play nicely into generic
189/// constructs.
190pub trait Linkable<S> {
191 /// Make this object listen for signals from given signaler
192 fn link(&mut self, signaler: Signaler<S>);
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 use std::{cell::Cell, rc::Rc};
200
201 #[test]
202 fn basic_signal() {
203 let signaler = Signaler::<u32>::new();
204
205 let signaled = Rc::new(Cell::new(false));
206 let signaled2 = signaled.clone();
207
208 let _token = signaler.register(move |_| signaled2.set(true));
209
210 signaler.signal(0);
211
212 assert!(signaled.get());
213 }
214
215 #[test]
216 fn remove_callback() {
217 let signaler = Signaler::<u32>::new();
218
219 let token = signaler.register(|&i| assert_eq!(i, 42));
220
221 signaler.signal(42);
222
223 ::std::mem::drop(token);
224
225 signaler.signal(41);
226
227 let _token = signaler.register(|&i| assert_eq!(i, 39));
228
229 signaler.signal(39);
230 }
231
232 #[test]
233 fn delayed_signal() {
234 let signaler = Signaler::<u32>::new();
235
236 let mut signaled = false;
237 let sign2 = signaler.clone();
238 let _token = signaler.register(move |&i| {
239 if !signaled {
240 sign2.signal(42);
241 signaled = true;
242 } else {
243 assert_eq!(i, 42);
244 }
245 });
246
247 signaler.signal(0);
248 }
249
250 #[test]
251 fn delayed_register() {
252 let signaler = Signaler::<bool>::new();
253
254 let signaled = Rc::new(Cell::new(0u32));
255 let signaled2 = signaled.clone();
256 let sign2 = signaler.clone();
257
258 let _token1 = signaler.register(move |&original| {
259 signaled2.set(signaled2.get() + 1);
260 if original {
261 let signaled3 = signaled2.clone();
262 sign2.register(move |_| signaled3.set(signaled3.get() + 1)).leak();
263 sign2.signal(false);
264 }
265 });
266
267 signaler.signal(true);
268
269 // Two rounds of signals, the first triggers 1 callback, the second triggers 2
270 assert_eq!(signaled.get(), 3);
271 }
272}