aboutsummaryrefslogtreecommitdiff
path: root/embassy-time/src/timer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-time/src/timer.rs')
-rw-r--r--embassy-time/src/timer.rs103
1 files changed, 85 insertions, 18 deletions
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs
index bc39d8bc7..2f5967c63 100644
--- a/embassy-time/src/timer.rs
+++ b/embassy-time/src/timer.rs
@@ -1,10 +1,9 @@
1use core::future::{poll_fn, Future}; 1use core::future::{Future, poll_fn};
2use core::pin::{pin, Pin}; 2use core::pin::Pin;
3use core::task::{Context, Poll}; 3use core::task::{Context, Poll};
4 4
5use futures_util::future::{select, Either}; 5use futures_core::Stream;
6use futures_util::stream::FusedStream; 6use futures_core::stream::FusedStream;
7use futures_util::Stream;
8 7
9use crate::{Duration, Instant}; 8use crate::{Duration, Instant};
10 9
@@ -17,11 +16,10 @@ pub struct TimeoutError;
17/// 16///
18/// If the future completes before the timeout, its output is returned. Otherwise, on timeout, 17/// If the future completes before the timeout, its output is returned. Otherwise, on timeout,
19/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. 18/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
20pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> { 19pub fn with_timeout<F: Future>(timeout: Duration, fut: F) -> TimeoutFuture<F> {
21 let timeout_fut = Timer::after(timeout); 20 TimeoutFuture {
22 match select(pin!(fut), timeout_fut).await { 21 timer: Timer::after(timeout),
23 Either::Left((r, _)) => Ok(r), 22 fut,
24 Either::Right(_) => Err(TimeoutError),
25 } 23 }
26} 24}
27 25
@@ -29,16 +27,75 @@ pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Out
29/// 27///
30/// If the future completes before the deadline, its output is returned. Otherwise, on timeout, 28/// If the future completes before the deadline, its output is returned. Otherwise, on timeout,
31/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. 29/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
32pub async fn with_deadline<F: Future>(at: Instant, fut: F) -> Result<F::Output, TimeoutError> { 30pub fn with_deadline<F: Future>(at: Instant, fut: F) -> TimeoutFuture<F> {
33 let timeout_fut = Timer::at(at); 31 TimeoutFuture {
34 match select(pin!(fut), timeout_fut).await { 32 timer: Timer::at(at),
35 Either::Left((r, _)) => Ok(r), 33 fut,
36 Either::Right(_) => Err(TimeoutError), 34 }
35}
36
37/// Provides functions to run a given future with a timeout or a deadline.
38pub trait WithTimeout: Sized {
39 /// Output type of the future.
40 type Output;
41
42 /// Runs a given future with a timeout.
43 ///
44 /// If the future completes before the timeout, its output is returned. Otherwise, on timeout,
45 /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
46 fn with_timeout(self, timeout: Duration) -> TimeoutFuture<Self>;
47
48 /// Runs a given future with a deadline time.
49 ///
50 /// If the future completes before the deadline, its output is returned. Otherwise, on timeout,
51 /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
52 fn with_deadline(self, at: Instant) -> TimeoutFuture<Self>;
53}
54
55impl<F: Future> WithTimeout for F {
56 type Output = F::Output;
57
58 fn with_timeout(self, timeout: Duration) -> TimeoutFuture<Self> {
59 with_timeout(timeout, self)
60 }
61
62 fn with_deadline(self, at: Instant) -> TimeoutFuture<Self> {
63 with_deadline(at, self)
64 }
65}
66
67/// Future for the [`with_timeout`] and [`with_deadline`] functions.
68#[must_use = "futures do nothing unless you `.await` or poll them"]
69#[derive(Debug)]
70#[cfg_attr(feature = "defmt", derive(defmt::Format))]
71pub struct TimeoutFuture<F> {
72 timer: Timer,
73 fut: F,
74}
75
76impl<F: Unpin> Unpin for TimeoutFuture<F> {}
77
78impl<F: Future> Future for TimeoutFuture<F> {
79 type Output = Result<F::Output, TimeoutError>;
80
81 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
82 let this = unsafe { self.get_unchecked_mut() };
83 let fut = unsafe { Pin::new_unchecked(&mut this.fut) };
84 let timer = unsafe { Pin::new_unchecked(&mut this.timer) };
85 if let Poll::Ready(x) = fut.poll(cx) {
86 return Poll::Ready(Ok(x));
87 }
88 if let Poll::Ready(_) = timer.poll(cx) {
89 return Poll::Ready(Err(TimeoutError));
90 }
91 Poll::Pending
37 } 92 }
38} 93}
39 94
40/// A future that completes at a specified [Instant](struct.Instant.html). 95/// A future that completes at a specified [Instant](struct.Instant.html).
41#[must_use = "futures do nothing unless you `.await` or poll them"] 96#[must_use = "futures do nothing unless you `.await` or poll them"]
97#[derive(Debug)]
98#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub struct Timer { 99pub struct Timer {
43 expires_at: Instant, 100 expires_at: Instant,
44 yielded_once: bool, 101 yielded_once: bool,
@@ -46,6 +103,7 @@ pub struct Timer {
46 103
47impl Timer { 104impl Timer {
48 /// Expire at specified [Instant](struct.Instant.html) 105 /// Expire at specified [Instant](struct.Instant.html)
106 /// Will expire immediately if the Instant is in the past.
49 pub fn at(expires_at: Instant) -> Self { 107 pub fn at(expires_at: Instant) -> Self {
50 Self { 108 Self {
51 expires_at, 109 expires_at,
@@ -127,7 +185,7 @@ impl Future for Timer {
127 if self.yielded_once && self.expires_at <= Instant::now() { 185 if self.yielded_once && self.expires_at <= Instant::now() {
128 Poll::Ready(()) 186 Poll::Ready(())
129 } else { 187 } else {
130 embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); 188 embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());
131 self.yielded_once = true; 189 self.yielded_once = true;
132 Poll::Pending 190 Poll::Pending
133 } 191 }
@@ -170,6 +228,12 @@ impl Future for Timer {
170/// } 228/// }
171/// } 229/// }
172/// ``` 230/// ```
231///
232/// ## Cancel safety
233/// It is safe to cancel waiting for the next tick,
234/// meaning no tick is lost if the Future is dropped.
235#[derive(Debug)]
236#[cfg_attr(feature = "defmt", derive(defmt::Format))]
173pub struct Ticker { 237pub struct Ticker {
174 expires_at: Instant, 238 expires_at: Instant,
175 duration: Duration, 239 duration: Duration,
@@ -201,6 +265,9 @@ impl Ticker {
201 } 265 }
202 266
203 /// Waits for the next tick. 267 /// Waits for the next tick.
268 ///
269 /// ## Cancel safety
270 /// The produced Future is cancel safe, meaning no tick is lost if the Future is dropped.
204 pub fn next(&mut self) -> impl Future<Output = ()> + Send + Sync + '_ { 271 pub fn next(&mut self) -> impl Future<Output = ()> + Send + Sync + '_ {
205 poll_fn(|cx| { 272 poll_fn(|cx| {
206 if self.expires_at <= Instant::now() { 273 if self.expires_at <= Instant::now() {
@@ -208,7 +275,7 @@ impl Ticker {
208 self.expires_at += dur; 275 self.expires_at += dur;
209 Poll::Ready(()) 276 Poll::Ready(())
210 } else { 277 } else {
211 embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); 278 embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());
212 Poll::Pending 279 Poll::Pending
213 } 280 }
214 }) 281 })
@@ -225,7 +292,7 @@ impl Stream for Ticker {
225 self.expires_at += dur; 292 self.expires_at += dur;
226 Poll::Ready(Some(())) 293 Poll::Ready(Some(()))
227 } else { 294 } else {
228 embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); 295 embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());
229 Poll::Pending 296 Poll::Pending
230 } 297 }
231 } 298 }