embassy_time/timer.rs
1use core::future::{poll_fn, Future};
2use core::pin::Pin;
3use core::task::{Context, Poll};
4
5use futures_core::stream::FusedStream;
6use futures_core::Stream;
7
8use crate::{Duration, Instant};
9
10/// Error returned by [`with_timeout`] and [`with_deadline`] on timeout.
11#[derive(Debug, Clone, PartialEq, Eq)]
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13pub struct TimeoutError;
14
15/// Runs a given future with a timeout.
16///
17/// If the future completes before the timeout, its output is returned. Otherwise, on timeout,
18/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
19pub fn with_timeout<F: Future>(timeout: Duration, fut: F) -> TimeoutFuture<F> {
20 TimeoutFuture {
21 timer: Timer::after(timeout),
22 fut,
23 }
24}
25
26/// Runs a given future with a deadline time.
27///
28/// If the future completes before the deadline, its output is returned. Otherwise, on timeout,
29/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
30pub fn with_deadline<F: Future>(at: Instant, fut: F) -> TimeoutFuture<F> {
31 TimeoutFuture {
32 timer: Timer::at(at),
33 fut,
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
92 }
93}
94
95/// A future that completes at a specified [Instant](struct.Instant.html).
96#[must_use = "futures do nothing unless you `.await` or poll them"]
97#[derive(Debug)]
98#[cfg_attr(feature = "defmt", derive(defmt::Format))]
99pub struct Timer {
100 expires_at: Instant,
101 yielded_once: bool,
102}
103
104impl Timer {
105 /// Expire at specified [Instant](struct.Instant.html)
106 /// Will expire immediately if the Instant is in the past.
107 pub fn at(expires_at: Instant) -> Self {
108 Self {
109 expires_at,
110 yielded_once: false,
111 }
112 }
113
114 /// Expire after specified [Duration](struct.Duration.html).
115 /// This can be used as a `sleep` abstraction.
116 ///
117 /// Example:
118 /// ``` no_run
119 /// use embassy_time::{Duration, Timer};
120 ///
121 /// #[embassy_executor::task]
122 /// async fn demo_sleep_seconds() {
123 /// // suspend this task for one second.
124 /// Timer::after(Duration::from_secs(1)).await;
125 /// }
126 /// ```
127 pub fn after(duration: Duration) -> Self {
128 Self {
129 expires_at: Instant::now() + duration,
130 yielded_once: false,
131 }
132 }
133
134 /// Expire after the specified number of ticks.
135 ///
136 /// This method is a convenience wrapper for calling `Timer::after(Duration::from_ticks())`.
137 /// For more details, refer to [`Timer::after()`] and [`Duration::from_ticks()`].
138 #[inline]
139 pub fn after_ticks(ticks: u64) -> Self {
140 Self::after(Duration::from_ticks(ticks))
141 }
142
143 /// Expire after the specified number of nanoseconds.
144 ///
145 /// This method is a convenience wrapper for calling `Timer::after(Duration::from_nanos())`.
146 /// For more details, refer to [`Timer::after()`] and [`Duration::from_nanos()`].
147 #[inline]
148 pub fn after_nanos(nanos: u64) -> Self {
149 Self::after(Duration::from_nanos(nanos))
150 }
151
152 /// Expire after the specified number of microseconds.
153 ///
154 /// This method is a convenience wrapper for calling `Timer::after(Duration::from_micros())`.
155 /// For more details, refer to [`Timer::after()`] and [`Duration::from_micros()`].
156 #[inline]
157 pub fn after_micros(micros: u64) -> Self {
158 Self::after(Duration::from_micros(micros))
159 }
160
161 /// Expire after the specified number of milliseconds.
162 ///
163 /// This method is a convenience wrapper for calling `Timer::after(Duration::from_millis())`.
164 /// For more details, refer to [`Timer::after`] and [`Duration::from_millis()`].
165 #[inline]
166 pub fn after_millis(millis: u64) -> Self {
167 Self::after(Duration::from_millis(millis))
168 }
169
170 /// Expire after the specified number of seconds.
171 ///
172 /// This method is a convenience wrapper for calling `Timer::after(Duration::from_secs())`.
173 /// For more details, refer to [`Timer::after`] and [`Duration::from_secs()`].
174 #[inline]
175 pub fn after_secs(secs: u64) -> Self {
176 Self::after(Duration::from_secs(secs))
177 }
178}
179
180impl Unpin for Timer {}
181
182impl Future for Timer {
183 type Output = ();
184 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
185 if self.yielded_once && self.expires_at <= Instant::now() {
186 Poll::Ready(())
187 } else {
188 embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());
189 self.yielded_once = true;
190 Poll::Pending
191 }
192 }
193}
194
195/// Asynchronous stream that yields every Duration, indefinitely.
196///
197/// This stream will tick at uniform intervals, even if blocking work is performed between ticks.
198///
199/// For instance, consider the following code fragment.
200/// ``` no_run
201/// use embassy_time::{Duration, Timer};
202/// # fn foo() {}
203///
204/// #[embassy_executor::task]
205/// async fn ticker_example_0() {
206/// loop {
207/// foo();
208/// Timer::after(Duration::from_secs(1)).await;
209/// }
210/// }
211/// ```
212///
213/// This fragment will not call `foo` every second.
214/// Instead, it will call it every second + the time it took to previously call `foo`.
215///
216/// Example using ticker, which will consistently call `foo` once a second.
217///
218/// ``` no_run
219/// use embassy_time::{Duration, Ticker};
220/// # fn foo(){}
221///
222/// #[embassy_executor::task]
223/// async fn ticker_example_1() {
224/// let mut ticker = Ticker::every(Duration::from_secs(1));
225/// loop {
226/// foo();
227/// ticker.next().await;
228/// }
229/// }
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))]
237pub struct Ticker {
238 expires_at: Instant,
239 duration: Duration,
240}
241
242impl Ticker {
243 /// Creates a new ticker that ticks at the specified duration interval.
244 pub fn every(duration: Duration) -> Self {
245 let expires_at = Instant::now() + duration;
246 Self { expires_at, duration }
247 }
248
249 /// Resets the ticker back to its original state.
250 /// This causes the ticker to go back to zero, even if the current tick isn't over yet.
251 pub fn reset(&mut self) {
252 self.expires_at = Instant::now() + self.duration;
253 }
254
255 /// Reset the ticker at the deadline.
256 /// If the deadline is in the past, the ticker will fire instantly.
257 pub fn reset_at(&mut self, deadline: Instant) {
258 self.expires_at = deadline + self.duration;
259 }
260
261 /// Resets the ticker, after the specified duration has passed.
262 /// If the specified duration is zero, the next tick will be after the duration of the ticker.
263 pub fn reset_after(&mut self, after: Duration) {
264 self.expires_at = Instant::now() + after + self.duration;
265 }
266
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.
271 pub fn next(&mut self) -> impl Future<Output = ()> + Send + Sync + '_ {
272 poll_fn(|cx| {
273 if self.expires_at <= Instant::now() {
274 let dur = self.duration;
275 self.expires_at += dur;
276 Poll::Ready(())
277 } else {
278 embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());
279 Poll::Pending
280 }
281 })
282 }
283}
284
285impl Unpin for Ticker {}
286
287impl Stream for Ticker {
288 type Item = ();
289 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
290 if self.expires_at <= Instant::now() {
291 let dur = self.duration;
292 self.expires_at += dur;
293 Poll::Ready(Some(()))
294 } else {
295 embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());
296 Poll::Pending
297 }
298 }
299}
300
301impl FusedStream for Ticker {
302 fn is_terminated(&self) -> bool {
303 // `Ticker` keeps yielding values until dropped, it never terminates.
304 false
305 }
306}