Skip to main content

juggle/
yield_helper.rs

1use core::future::Future;
2use core::pin::Pin;
3use core::task::{Context, Poll};
4
5/// Helper struct for dealing with task switching.
6#[derive(Clone, Debug, Hash, Eq, PartialEq)]
7pub struct Yield(bool);
8
9#[doc(hidden)]
10#[derive(Clone, Debug, Hash, Eq, PartialEq)]
11pub struct YieldWhile<F: FnMut() -> bool>(F);
12
13#[doc(hidden)]
14#[derive(Clone, Debug, Hash, Eq, PartialEq)]
15pub struct YieldTimes { pub remaining: usize }
16
17impl Yield {
18    /// When awaited yields this task once. Causes task switch.
19    ///
20    /// For more convenient method of switching tasks see [`yield_once!()`](macro.yield_once.html) macro.
21    ///
22    /// When resulting Future is polled, for the first time it notifies the waker and returns
23    /// `Poll::Pending`, second and all other polls return `Poll::Ready(())`.
24    pub fn once() -> Self { Self(false) }
25
26    /// When awaited it won't cause task switch.
27    ///
28    /// Future returned by this method when polled always return `Poll::Ready(())`.
29    pub fn none() -> Self { Self(true) }
30
31    /// When awaited yields this task specific number of times.
32    ///
33    /// Resulting Future notifies the waker and returns
34    /// `Poll::Pending` 'remaining' number of times, all other polls return `Poll::Ready(())`.
35    pub fn times(remaining: usize) -> YieldTimes { YieldTimes { remaining } }
36
37    /// When awaited yields this task until provided closure returns false.
38    ///
39    /// Note that when first call on closure returns false, this task will not be yielded.
40    /// This method is usefull when we want to do busy wait but also leave cpu time for
41    /// other tasks.
42    /// # Examples
43    /// ```
44    /// # fn main(){
45    /// # use juggle::Yield;
46    /// # use core::sync::atomic::{AtomicBool, Ordering};
47    /// # smol::block_on(async move{
48    /// let interrupt_flag: &AtomicBool = //...
49    /// # &AtomicBool::new(true);
50    ///
51    /// Yield::yield_while(||!interrupt_flag.load(Ordering::Acquire)).await;
52    /// # });
53    /// # }
54    /// ```
55    pub fn yield_while<F>(predicate: F) -> YieldWhile<F> where F: FnMut() -> bool {
56        YieldWhile(predicate)
57    }
58}
59
60impl Future for Yield {
61    type Output = ();
62    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
63        if self.0 { Poll::Ready(()) } else {
64            self.get_mut().0 = true;
65            cx.waker().wake_by_ref();
66            Poll::Pending
67        }
68    }
69}
70
71impl<F: FnMut() -> bool> Future for YieldWhile<F> {
72    type Output = ();
73    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
74        //SAFETY: F does not care about being pinned
75        let func = unsafe{ &mut self.get_unchecked_mut().0 };
76        if !func() { Poll::Ready(()) } else {
77            cx.waker().wake_by_ref();
78            Poll::Pending
79        }
80    }
81}
82
83impl Future for YieldTimes {
84    type Output = ();
85    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
86        if self.remaining == 0 { Poll::Ready(()) } else {
87            self.as_mut().remaining -= 1;
88            cx.waker().wake_by_ref();
89            Poll::Pending
90        }
91    }
92}