diff options
| author | Alex Moon <[email protected]> | 2025-04-23 13:15:17 -0400 |
|---|---|---|
| committer | Alex Moon <[email protected]> | 2025-04-23 13:32:06 -0400 |
| commit | 0b8f43b714ab8ae951740a9e45c04d7a855c2067 (patch) | |
| tree | 76b673d704e10c9ae976de98f8ebe9c34a081786 /embassy-time/src/timer.rs | |
| parent | 9907f5683bc3d2c436e03143b32c20c41251f457 (diff) | |
Manually implement the future for `with_timeout`
Diffstat (limited to 'embassy-time/src/timer.rs')
| -rw-r--r-- | embassy-time/src/timer.rs | 61 |
1 files changed, 42 insertions, 19 deletions
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 34e5762d2..d1162eadd 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs | |||
| @@ -1,8 +1,7 @@ | |||
| 1 | use core::future::{poll_fn, Future}; | 1 | use core::future::{poll_fn, Future}; |
| 2 | use core::pin::{pin, Pin}; | 2 | use core::pin::Pin; |
| 3 | use core::task::{Context, Poll}; | 3 | use core::task::{Context, Poll}; |
| 4 | 4 | ||
| 5 | use futures_util::future::{select, Either}; | ||
| 6 | use futures_util::stream::FusedStream; | 5 | use futures_util::stream::FusedStream; |
| 7 | use futures_util::Stream; | 6 | use futures_util::Stream; |
| 8 | 7 | ||
| @@ -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. |
| 20 | pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> { | 19 | pub 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,15 @@ 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. |
| 32 | pub async fn with_deadline<F: Future>(at: Instant, fut: F) -> Result<F::Output, TimeoutError> { | 30 | pub 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), | ||
| 37 | } | 34 | } |
| 38 | } | 35 | } |
| 39 | 36 | ||
| 40 | /// Provides functions to run a given future with a timeout or a deadline. | 37 | /// Provides functions to run a given future with a timeout or a deadline. |
| 41 | pub trait WithTimeout { | 38 | pub trait WithTimeout: Sized { |
| 42 | /// Output type of the future. | 39 | /// Output type of the future. |
| 43 | type Output; | 40 | type Output; |
| 44 | 41 | ||
| @@ -46,24 +43,50 @@ pub trait WithTimeout { | |||
| 46 | /// | 43 | /// |
| 47 | /// If the future completes before the timeout, its output is returned. Otherwise, on timeout, | 44 | /// If the future completes before the timeout, its output is returned. Otherwise, on timeout, |
| 48 | /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. | 45 | /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. |
| 49 | async fn with_timeout(self, timeout: Duration) -> Result<Self::Output, TimeoutError>; | 46 | fn with_timeout(self, timeout: Duration) -> TimeoutFuture<Self>; |
| 50 | 47 | ||
| 51 | /// Runs a given future with a deadline time. | 48 | /// Runs a given future with a deadline time. |
| 52 | /// | 49 | /// |
| 53 | /// If the future completes before the deadline, its output is returned. Otherwise, on timeout, | 50 | /// If the future completes before the deadline, its output is returned. Otherwise, on timeout, |
| 54 | /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. | 51 | /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. |
| 55 | async fn with_deadline(self, at: Instant) -> Result<Self::Output, TimeoutError>; | 52 | fn with_deadline(self, at: Instant) -> TimeoutFuture<Self>; |
| 56 | } | 53 | } |
| 57 | 54 | ||
| 58 | impl<F: Future> WithTimeout for F { | 55 | impl<F: Future> WithTimeout for F { |
| 59 | type Output = F::Output; | 56 | type Output = F::Output; |
| 60 | 57 | ||
| 61 | async fn with_timeout(self, timeout: Duration) -> Result<Self::Output, TimeoutError> { | 58 | fn with_timeout(self, timeout: Duration) -> TimeoutFuture<Self> { |
| 62 | with_timeout(timeout, self).await | 59 | with_timeout(timeout, self) |
| 63 | } | 60 | } |
| 64 | 61 | ||
| 65 | async fn with_deadline(self, at: Instant) -> Result<Self::Output, TimeoutError> { | 62 | fn with_deadline(self, at: Instant) -> TimeoutFuture<Self> { |
| 66 | with_deadline(at, self).await | 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 | pub struct TimeoutFuture<F> { | ||
| 70 | timer: Timer, | ||
| 71 | fut: F, | ||
| 72 | } | ||
| 73 | |||
| 74 | impl<F: Unpin> Unpin for TimeoutFuture<F> {} | ||
| 75 | |||
| 76 | impl<F: Future> Future for TimeoutFuture<F> { | ||
| 77 | type Output = Result<F::Output, TimeoutError>; | ||
| 78 | |||
| 79 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 80 | let this = unsafe { self.get_unchecked_mut() }; | ||
| 81 | let fut = unsafe { Pin::new_unchecked(&mut this.fut) }; | ||
| 82 | let timer = unsafe { Pin::new_unchecked(&mut this.timer) }; | ||
| 83 | if let Poll::Ready(x) = fut.poll(cx) { | ||
| 84 | return Poll::Ready(Ok(x)); | ||
| 85 | } | ||
| 86 | if let Poll::Ready(_) = timer.poll(cx) { | ||
| 87 | return Poll::Ready(Err(TimeoutError)); | ||
| 88 | } | ||
| 89 | Poll::Pending | ||
| 67 | } | 90 | } |
| 68 | } | 91 | } |
| 69 | 92 | ||
