wookie/
wookie.rs

1use super::Stats;
2use alloc::sync::Arc;
3use core::future::Future;
4use core::mem::ManuallyDrop;
5use core::pin::Pin;
6use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
7use core::sync::atomic::{AtomicU16, Ordering::Relaxed};
8
9/// A single-future stepping executor for test suites that tracks wakers.
10///
11/// ## Examples
12///
13/// ```
14/// use core::task::Poll;
15/// use wookie::wookie;
16/// wookie!(future: async { true });
17/// assert_eq!(future.poll(), Poll::Ready(true));
18///
19/// // you can also just give a variable name if you have one:
20/// let future = async { true };
21/// wookie!(future);
22/// assert_eq!(future.poll(), Poll::Ready(true));
23///
24/// // we can find out about the state of wakers any time:
25/// assert_eq!(future.cloned(), 0);
26/// assert_eq!(future.dropped(), 0);
27/// assert_eq!(future.woken(), 0);
28/// // or equivalently...
29/// future.stats().assert(0, 0, 0);
30/// ```
31pub struct Wookie<F> {
32    wakey: Arc<Wakey>,
33    ptr: *const Wakey,
34    future: F,
35}
36
37
38impl<F: Future> Wookie<F> {
39
40    /// Creates a new [`Wookie`] without pinning it to the stack. You
41    /// probably want the [`crate::wookie!`] macro.
42    #[inline(always)]
43    pub fn new(future: F) -> Wookie<F> {
44        let ptr = Arc::into_raw(Arc::new(Wakey::default()));
45        let wakey = unsafe { Arc::from_raw(ptr) };
46        Wookie { wakey, ptr, future }
47    }
48
49    /// Returns how many times the waker has been woken. This count is
50    /// cumulative, it is never reset and is allowed to overflow.
51    #[inline(always)]
52    pub fn woken(self: &mut Pin<&mut Self>) -> u16 {
53        self.as_mut().project().wakey.woken.load(Relaxed)
54    }
55
56    /// Returns how many times the waker has been cloned. This count is
57    /// cumulative, it is never reset and is allowed to overflow.
58    #[inline(always)]
59    pub fn cloned(self: &mut Pin<&mut Self>) -> u16 {
60        self.as_mut().project().wakey.cloned.load(Relaxed)
61    }
62
63    /// Returns how many times a clone of the waker has been
64    /// dropped. This count is cumulative, it is never reset and is
65    /// allowed to overflow.
66    #[inline(always)]
67    pub fn dropped(self: &mut Pin<&mut Self>) -> u16 {
68        self.as_mut().project().wakey.dropped.load(Relaxed)
69    }
70
71    /// Returns statistics about use of our wakers.
72    #[inline(always)]
73    pub fn stats(self: &mut Pin<&mut Self>) -> Stats {
74        let wakey = self.as_mut().project().wakey.as_ref();
75        Stats {
76            cloned:  wakey.cloned.load(Relaxed),
77            dropped: wakey.dropped.load(Relaxed),
78            woken:   wakey.woken.load(Relaxed),
79        }
80    }
81    /// Returns how many times a clone of the waker has been
82    /// dropped. This count is cumulative, it is never reset and is
83    /// allowed to overflow.
84    #[inline(always)]
85    pub fn live(self: &mut Pin<&mut Self>) -> u16 {
86        let wakey = self.as_mut().project().wakey.as_ref();
87        wakey.cloned.load(Relaxed) - wakey.dropped.load(Relaxed)
88    }
89
90    /// Polls the contained future once.
91    ///
92    /// ## Example
93    ///
94    /// ```
95    /// use core::task::Poll;
96    /// use wookie::wookie;
97    /// wookie!(future: async { true });
98    /// assert_eq!(future.poll(), Poll::Ready(true));
99    /// ```
100    #[inline(always)]
101    pub fn poll(
102        self: &mut Pin<&mut Self>
103    ) -> Poll<<F as Future>::Output> {
104        let this = self.as_mut().project();
105        let waker = ManuallyDrop::new(this.waker());
106        let future = unsafe { Pin::new_unchecked(&mut this.future) };
107        let mut ctx = Context::from_waker(&waker);
108        Future::poll(future, &mut ctx)
109    }
110
111    /// Polls the contained future until completion, so long as the
112    /// previous poll caused one or more wakes.
113    ///
114    /// ## Example
115    ///
116    /// ```
117    /// use core::task::Poll;
118    /// use wookie::wookie;
119    /// wookie!(future: async { true });
120    /// assert_eq!(future.poll_while_woken(), Poll::Ready(true));
121    /// ```
122    #[inline(always)]
123    pub fn poll_while_woken(
124        self: &mut Pin<&mut Self>
125    ) -> Poll<<F as Future>::Output> {
126        let mut woken = self.woken();
127        loop {
128            if let Poll::Ready(r) = self.poll() { return Poll::Ready(r); }
129            let w = self.woken();
130            if w == woken { return Poll::Pending; }
131            woken = w;
132        }
133    }
134
135    #[inline(always)]
136    fn waker(&self) -> Waker {
137        // Safety: the returned waker is valid as long as self is
138        // valid. But in order to do anything mutable with the Waker,
139        // they would have to have cloned it first.
140        let raw = wookie_rawwaker(self.ptr);
141        unsafe { Waker::from_raw(raw) }
142    }
143
144    #[inline(always)]
145    fn project(self: Pin<&mut Self>) -> &mut Self {
146        unsafe { Pin::into_inner_unchecked(self) }
147    }
148
149}
150
151
152/// Wraps a future in a single-future stepping executor for test
153/// suites that tracks wakers and pins it on the stack.
154///
155/// ## Examples
156///
157/// ```
158/// use core::task::Poll;
159/// use wookie::wookie;
160/// wookie!(future: async { true });
161/// assert_eq!(future.poll(), Poll::Ready(true));
162///
163/// // you can also just give a variable name if you have one:
164/// let future = async { true };
165/// wookie!(future);
166/// assert_eq!(future.poll(), Poll::Ready(true));
167///
168/// // we can find out about the state of wakers any time:
169/// assert_eq!(future.cloned(), 0);
170/// assert_eq!(future.dropped(), 0);
171/// assert_eq!(future.woken(), 0);
172/// // or equivalently...
173/// future.stats().assert(0, 0, 0);
174/// ```
175#[macro_export]
176macro_rules! wookie {
177    ($name:ident) => {
178        let mut $name = unsafe { $crate::Wookie::new($name) };
179        #[allow(unused_mut)]
180        let mut $name = unsafe { core::pin::Pin::new_unchecked(&mut $name) };
181    };
182    ($name:ident : $future:expr) => {
183        let mut $name = unsafe { $crate::Wookie::new($future) };
184        #[allow(unused_mut)]
185        let mut $name = unsafe { core::pin::Pin::new_unchecked(&mut $name) };
186    }
187}
188
189#[derive(Default)]
190struct Wakey {
191    cloned:  AtomicU16,
192    dropped: AtomicU16,
193    woken:   AtomicU16,
194}
195
196impl Wakey {
197    fn bump_cloned(&self)  -> u16 { self.cloned.fetch_add(1, Relaxed) }
198    fn bump_woken(&self)   -> u16 { self.woken.fetch_add(1, Relaxed) }
199    fn bump_dropped(&self) -> u16 { self.dropped.fetch_add(1, Relaxed) }
200}
201
202fn wookie_rawwaker(wakey: *const Wakey) -> RawWaker {
203    fn do_clone(data: *const ()) -> RawWaker {
204        let wakey = data as *const Wakey;
205        unsafe { &*wakey }.bump_cloned();
206        unsafe { Arc::increment_strong_count(wakey) };
207        wookie_rawwaker(wakey)
208    }
209
210    fn do_wake(data: *const ()) {
211        let wakey: Arc<Wakey> = unsafe { Arc::from_raw(data as *const Wakey) };
212        wakey.bump_woken();
213        wakey.bump_dropped();
214    }
215
216    fn do_wake_by_ref(data: *const ()) {
217        let arc = unsafe { Arc::from_raw(data as *const Wakey) };
218        let wakey = ManuallyDrop::new(arc);
219        wakey.bump_woken();
220    }
221
222    fn do_drop(data: *const ()) {
223        let wakey: Arc<Wakey> = unsafe { Arc::from_raw(data as *const Wakey) };
224        wakey.bump_dropped();
225    }
226
227    RawWaker::new(
228        wakey as *const (),
229        &RawWakerVTable::new(do_clone, do_wake, do_wake_by_ref, do_drop)
230    )
231}
232