Skip to main content

tokio/signal/
registry.rs

1use crate::signal::unix::{OsExtraData, OsStorage};
2use crate::sync::watch;
3
4use std::ops;
5use std::sync::atomic::{AtomicBool, Ordering};
6use std::sync::OnceLock;
7
8pub(crate) type EventId = usize;
9
10/// State for a specific event, whether a notification is pending delivery,
11/// and what listeners are registered.
12#[derive(Debug)]
13pub(crate) struct EventInfo {
14    pending: AtomicBool,
15    tx: watch::Sender<()>,
16}
17
18impl Default for EventInfo {
19    fn default() -> Self {
20        let (tx, _rx) = watch::channel(());
21
22        Self {
23            pending: AtomicBool::new(false),
24            tx,
25        }
26    }
27}
28
29/// An interface for retrieving the `EventInfo` for a particular `eventId`.
30pub(crate) trait Storage {
31    /// Gets the `EventInfo` for `id` if it exists.
32    fn event_info(&self, id: EventId) -> Option<&EventInfo>;
33
34    /// Returns an iterator over the storage.
35    fn iter(&self) -> impl Iterator<Item = &EventInfo>;
36}
37
38impl Storage for Vec<EventInfo> {
39    fn event_info(&self, id: EventId) -> Option<&EventInfo> {
40        self.get(id)
41    }
42
43    fn iter(&self) -> impl Iterator<Item = &EventInfo> {
44        self.as_slice().iter()
45    }
46}
47
48/// Manages and distributes event notifications to any registered listeners.
49///
50/// Generic over the underlying storage to allow for domain specific
51/// optimizations (e.g. `eventIds` may or may not be contiguous).
52#[derive(Debug)]
53pub(crate) struct Registry<S> {
54    storage: S,
55}
56
57impl<S> Registry<S> {
58    fn new(storage: S) -> Self {
59        Self { storage }
60    }
61}
62
63impl<S: Storage> Registry<S> {
64    /// Registers a new listener for `event_id`.
65    fn register_listener(&self, event_id: EventId) -> watch::Receiver<()> {
66        self.storage
67            .event_info(event_id)
68            .unwrap_or_else(|| panic!("invalid event_id: {event_id}"))
69            .tx
70            .subscribe()
71    }
72
73    /// Marks `event_id` as having been delivered, without broadcasting it to
74    /// any listeners.
75    fn record_event(&self, event_id: EventId) {
76        if let Some(event_info) = self.storage.event_info(event_id) {
77            event_info.pending.store(true, Ordering::SeqCst);
78        }
79    }
80
81    /// Broadcasts all previously recorded events to their respective listeners.
82    ///
83    /// Returns `true` if an event was delivered to at least one listener.
84    fn broadcast(&self) -> bool {
85        self.storage
86            .iter()
87            // Any signal of this kind arrived since we checked last?
88            .filter(|event_info| event_info.pending.swap(false, Ordering::SeqCst))
89            // Ignore errors if there are no listeners
90            .map(|event_info| event_info.tx.send(()).is_ok())
91            .fold(false, |acc, x| acc | x)
92    }
93}
94
95pub(crate) struct Globals {
96    extra: OsExtraData,
97    registry: Registry<OsStorage>,
98}
99
100impl ops::Deref for Globals {
101    type Target = OsExtraData;
102
103    fn deref(&self) -> &Self::Target {
104        &self.extra
105    }
106}
107
108impl Globals {
109    /// Registers a new listener for `event_id`.
110    pub(crate) fn register_listener(&self, event_id: EventId) -> watch::Receiver<()> {
111        self.registry.register_listener(event_id)
112    }
113
114    /// Marks `event_id` as having been delivered, without broadcasting it to
115    /// any listeners.
116    pub(crate) fn record_event(&self, event_id: EventId) {
117        self.registry.record_event(event_id);
118    }
119
120    /// Broadcasts all previously recorded events to their respective listeners.
121    ///
122    /// Returns `true` if an event was delivered to at least one listener.
123    pub(crate) fn broadcast(&self) -> bool {
124        self.registry.broadcast()
125    }
126
127    #[cfg(unix)]
128    pub(crate) fn storage(&self) -> &OsStorage {
129        &self.registry.storage
130    }
131}
132
133fn globals_init() -> Globals
134where
135    OsExtraData: 'static + Send + Sync + Default,
136    OsStorage: 'static + Send + Sync + Default,
137{
138    Globals {
139        extra: OsExtraData::default(),
140        registry: Registry::new(OsStorage::default()),
141    }
142}
143
144pub(crate) fn globals() -> &'static Globals
145where
146    OsExtraData: 'static + Send + Sync + Default,
147    OsStorage: 'static + Send + Sync + Default,
148{
149    static GLOBALS: OnceLock<Globals> = OnceLock::new();
150
151    GLOBALS.get_or_init(globals_init)
152}
153
154#[cfg(all(test, not(loom)))]
155mod tests {
156    use super::*;
157    use crate::runtime::{self, Runtime};
158    use crate::sync::{oneshot, watch};
159
160    use futures::future;
161
162    #[test]
163    fn smoke() {
164        let rt = rt();
165        rt.block_on(async move {
166            let registry = Registry::new(vec![
167                EventInfo::default(),
168                EventInfo::default(),
169                EventInfo::default(),
170            ]);
171
172            let first = registry.register_listener(0);
173            let second = registry.register_listener(1);
174            let third = registry.register_listener(2);
175
176            let (fire, wait) = oneshot::channel();
177
178            crate::spawn(async {
179                wait.await.expect("wait failed");
180
181                // Record some events which should get coalesced
182                registry.record_event(0);
183                registry.record_event(0);
184                registry.record_event(1);
185                registry.record_event(1);
186                registry.broadcast();
187
188                // Yield so the previous broadcast can get received
189                //
190                // This yields many times since the block_on task is only polled every 61
191                // ticks.
192                for _ in 0..100 {
193                    crate::task::yield_now().await;
194                }
195
196                // Send subsequent signal
197                registry.record_event(0);
198                registry.broadcast();
199
200                drop(registry);
201            });
202
203            let _ = fire.send(());
204            let all = future::join3(collect(first), collect(second), collect(third));
205
206            let (first_results, second_results, third_results) = all.await;
207            assert_eq!(2, first_results.len());
208            assert_eq!(1, second_results.len());
209            assert_eq!(0, third_results.len());
210        });
211    }
212
213    #[test]
214    #[should_panic = "invalid event_id: 1"]
215    fn register_panics_on_invalid_input() {
216        let registry = Registry::new(vec![EventInfo::default()]);
217
218        registry.register_listener(1);
219    }
220
221    #[test]
222    fn record_invalid_event_does_nothing() {
223        let registry = Registry::new(vec![EventInfo::default()]);
224        registry.record_event(1302);
225    }
226
227    #[test]
228    fn broadcast_returns_if_at_least_one_event_fired() {
229        let registry = Registry::new(vec![EventInfo::default(), EventInfo::default()]);
230
231        registry.record_event(0);
232        assert!(!registry.broadcast());
233
234        let first = registry.register_listener(0);
235        let second = registry.register_listener(1);
236
237        registry.record_event(0);
238        assert!(registry.broadcast());
239
240        drop(first);
241        registry.record_event(0);
242        assert!(!registry.broadcast());
243
244        drop(second);
245    }
246
247    fn rt() -> Runtime {
248        runtime::Builder::new_current_thread()
249            .enable_time()
250            .build()
251            .unwrap()
252    }
253
254    async fn collect(mut rx: watch::Receiver<()>) -> Vec<()> {
255        let mut ret = vec![];
256
257        while let Ok(v) = rx.changed().await {
258            ret.push(v);
259        }
260
261        ret
262    }
263}