winio_callback/
lib.rs

1//! A callback helper for async.
2
3#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
4#![warn(missing_docs)]
5
6use std::{
7    cell::RefCell,
8    future::Future,
9    hint::unreachable_unchecked,
10    pin::Pin,
11    task::{Context, Poll, Waker},
12};
13
14/// An abstract for global runtime.
15pub trait Runnable {
16    /// It will be called if the callback is signaled, and there's a waker to be
17    /// waked.
18    fn run();
19}
20
21impl Runnable for () {
22    fn run() {}
23}
24
25#[derive(Debug)]
26enum WakerState<T> {
27    Inactive,
28    Active(Waker),
29    Signaled(T),
30}
31
32/// A callback type. It is usually signaled in a GUI widget callback.
33#[derive(Debug)]
34pub struct Callback<T = ()>(RefCell<WakerState<T>>);
35
36impl<T> Default for Callback<T> {
37    fn default() -> Self {
38        Self::new()
39    }
40}
41
42impl<T> Callback<T> {
43    /// Create [`Callback`].
44    pub fn new() -> Self {
45        Self(RefCell::new(WakerState::Inactive))
46    }
47
48    /// Signal the callback and try to run the runtime if there's a waker
49    /// waiting. Returns `true` if not handled.
50    pub fn signal<R: Runnable>(&self, v: T) -> bool {
51        let mut state = self.0.borrow_mut();
52        match &*state {
53            WakerState::Inactive => return true,
54            WakerState::Signaled(_) => {
55                // If a state is signaled again, the runtime might be too busy
56                // to wake the waker. Just try to run it again.
57            }
58            WakerState::Active(waker) => {
59                waker.wake_by_ref();
60            }
61        }
62        *state = WakerState::Signaled(v);
63        drop(state);
64        R::run();
65        false
66    }
67
68    pub(crate) fn register(&self, waker: &Waker) -> Poll<T> {
69        let mut state = self.0.borrow_mut();
70        match &*state {
71            WakerState::Signaled(_) => {
72                let state = std::mem::replace(&mut *state, WakerState::Inactive);
73                let v = if let WakerState::Signaled(v) = state {
74                    v
75                } else {
76                    // SAFETY: already checked
77                    unsafe { unreachable_unchecked() }
78                };
79                Poll::Ready(v)
80            }
81            _ => {
82                *state = WakerState::Active(waker.clone());
83                Poll::Pending
84            }
85        }
86    }
87
88    /// Wait for signal.
89    pub fn wait(&self) -> impl Future<Output = T> + '_ {
90        WaitFut(self)
91    }
92}
93
94struct WaitFut<'a, T>(&'a Callback<T>);
95
96impl<T> Future for WaitFut<'_, T> {
97    type Output = T;
98
99    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
100        self.0.register(cx.waker())
101    }
102}
103
104impl<T> Drop for WaitFut<'_, T> {
105    fn drop(&mut self) {
106        // Deregister the waker.
107        *self.0.0.borrow_mut() = WakerState::Inactive;
108    }
109}