Skip to main content

tiny_notify/
lib.rs

1//! Wait until work is ready or something changes, without repeatedly checking.
2//! Choose `Notify` or `ArcNotify` for one waiting caller, or `BroadcastNotify` for several.
3
4use std::{
5    cell::{
6        Cell,
7        RefCell,
8    },
9    future::Future,
10    pin::Pin,
11    rc::Rc,
12    sync::{
13        Arc,
14        Mutex,
15        atomic::AtomicBool,
16    },
17    task::{
18        Context,
19        Poll,
20        Waker,
21    },
22};
23
24/// Wait for work without repeatedly asking whether it is ready, such as waiting for a new frame.
25/// Only one caller should wait at a time. For a wake-up from background work, use [`ArcNotify`].
26#[derive(Clone, Default)]
27pub struct Notify {
28    state: Rc<State>,
29}
30
31#[derive(Default)]
32struct State {
33    flag: Cell<bool>,
34    waker: Cell<Option<Waker>>,
35}
36
37impl Notify {
38    pub fn new() -> Self {
39        Self {
40            state: Rc::new(State {
41                flag: Cell::new(false),
42                waker: Cell::new(None),
43            }),
44        }
45    }
46
47    /// Wakes the waiting caller, or saves one wake-up for later if nobody is waiting.
48    /// Repeated calls count as one until the wake-up is received, so this does not count work items.
49    pub fn notify(&self) {
50        self.state.flag.set(true);
51
52        if let Some(w) = self.state.waker.take() {
53            w.wake();
54        }
55    }
56
57    /// Waits for a wake-up, including one saved before this call.
58    pub fn notified(&self) -> Notified {
59        Notified {
60            state: self.state.clone(),
61        }
62    }
63}
64
65pub struct Notified {
66    state: Rc<State>,
67}
68
69impl Future for Notified {
70    type Output = ();
71
72    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
73        if self.state.flag.replace(false) {
74            Poll::Ready(())
75        } else {
76            self.state.waker.set(Some(cx.waker().clone()));
77            Poll::Pending
78        }
79    }
80}
81
82/// Let background work wake the part of your program that needs its results.
83/// Only one caller should wait at a time. Store the results separately from the wake-up.
84#[derive(Clone, Default)]
85pub struct ArcNotify {
86    state: Arc<StateArc>,
87}
88
89#[derive(Default)]
90struct StateArc {
91    flag: AtomicBool,
92    waker: Mutex<Option<Waker>>,
93}
94
95impl ArcNotify {
96    pub fn new() -> Self {
97        Self::default()
98    }
99
100    /// Wakes the waiting caller, or saves one wake-up for later if nobody is waiting.
101    /// Repeated calls count as one until the wake-up is received, so this does not count work items.
102    pub fn notify(&self) {
103        self.state
104            .flag
105            .store(true, std::sync::atomic::Ordering::SeqCst);
106
107        if let Ok(mut w) = self.state.waker.lock()
108            && let Some(waker) = w.take()
109        {
110            waker.wake();
111        }
112    }
113
114    /// Waits for a wake-up, including one saved before this call.
115    pub fn notified(&self) -> NotifiedArc {
116        NotifiedArc {
117            state: self.state.clone(),
118        }
119    }
120}
121
122pub struct NotifiedArc {
123    state: Arc<StateArc>,
124}
125
126impl Future for NotifiedArc {
127    type Output = ();
128
129    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
130        if self
131            .state
132            .flag
133            .swap(false, std::sync::atomic::Ordering::SeqCst)
134        {
135            Poll::Ready(())
136        } else {
137            if let Ok(mut w) = self.state.waker.lock() {
138                *w = Some(cx.waker().clone());
139            }
140            Poll::Pending
141        }
142    }
143}
144
145/// Wake several parts of your program for the same change, such as the start of a new frame.
146/// Unlike [`Notify`], each notification can wake all waiting callers, not just one.
147#[derive(Clone, Default)]
148pub struct BroadcastNotify {
149    state: Rc<BroadcastState>,
150}
151
152#[derive(Default)]
153struct BroadcastState {
154    version: Cell<u64>,
155    wakers: RefCell<Vec<Waker>>,
156}
157
158impl BroadcastNotify {
159    pub fn new() -> Self {
160        Self::default()
161    }
162
163    /// Wakes every caller whose wait was created before this call.
164    /// Callers that start waiting afterward will wait for the next wake-up instead.
165    pub fn notify(&self) {
166        self.state
167            .version
168            .set(self.state.version.get().wrapping_add(1));
169        for waker in self.state.wakers.take() {
170            waker.wake();
171        }
172    }
173
174    /// Waits for the next wake-up after this call, even if `.await` comes later.
175    pub fn notified(&self) -> NotifiedBroadcast {
176        NotifiedBroadcast {
177            state: self.state.clone(),
178            version: self.state.version.get(),
179        }
180    }
181}
182
183pub struct NotifiedBroadcast {
184    state: Rc<BroadcastState>,
185    version: u64,
186}
187
188impl Future for NotifiedBroadcast {
189    type Output = ();
190
191    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
192        if self.state.version.get() != self.version {
193            Poll::Ready(())
194        } else {
195            let mut wakers = self.state.wakers.borrow_mut();
196            let waker = cx.waker();
197            if !wakers.iter().any(|registered| registered.will_wake(waker)) {
198                wakers.push(waker.clone());
199            }
200            Poll::Pending
201        }
202    }
203}
204
205#[cfg(test)]
206mod test {
207    use std::pin::pin;
208
209    use futures_lite::future::{
210        block_on,
211        poll_once,
212    };
213
214    use crate::BroadcastNotify;
215
216    #[test]
217    fn broadcast_notify() {
218        block_on(async {
219            let notify = BroadcastNotify::new();
220            let mut first = pin!(notify.notified());
221            let mut second = pin!(notify.notified());
222            assert_eq!(poll_once(&mut first).await, None);
223            assert_eq!(poll_once(&mut second).await, None);
224            notify.notify();
225            assert_eq!(poll_once(&mut first).await, Some(()));
226            assert_eq!(poll_once(&mut second).await, Some(()));
227
228            // A cancelled waiter must not complete a waiter registered later
229            {
230                let mut cancelled = pin!(notify.notified());
231                assert_eq!(poll_once(&mut cancelled).await, None);
232                notify.notify();
233            }
234            let mut late = pin!(notify.notified());
235            assert_eq!(poll_once(&mut late).await, None);
236            notify.notify();
237            assert_eq!(poll_once(&mut late).await, Some(()));
238        });
239    }
240}