async_io_mini/
timer.rs

1use core::fmt::{self, Debug};
2use core::future::Future;
3use core::pin::Pin;
4use core::task::{Context, Poll, Waker};
5use core::time::Duration;
6
7use std::time::Instant;
8
9/// A future or stream that emits timed events.
10///
11/// Timers are futures that output a single [`Instant`] when they fire.
12///
13/// Timers are also streams that can output [`Instant`]s periodically.
14///
15/// # Precision
16///
17/// There is a limit on the maximum precision that a `Timer` can provide. This limit is
18/// dependent on the current platform and follows the precision provided by the `embassy-time`
19/// crate for that platform; for instance, on Windows, the maximum precision is
20/// about 16 milliseconds. Because of this limit, the timer may sleep for longer than the
21/// requested duration. It will never sleep for less.
22///
23/// On embedded platforms like ESP-IDF, the precision is much higer (up to 1 microsecond),
24/// because the `embassy-time` crate for ESP-IDF uses the ESP-IDF Timer service.
25///
26/// # Examples
27///
28/// Sleep for 1 second:
29///
30/// ```
31/// use async_io_mini::Timer;
32/// use std::time::Duration;
33///
34/// # futures_lite::future::block_on(async {
35/// Timer::after(Duration::from_secs(1)).await;
36/// # });
37/// ```
38///
39/// Timeout after 1 second:
40///
41/// ```
42/// use async_io_mini::Timer;
43/// use futures_lite::FutureExt;
44/// use std::time::Duration;
45///
46/// # futures_lite::future::block_on(async {
47/// let wait = core::future::pending::<Result<(), std::io::Error>>()
48///     .or(async {
49///         Timer::after(Duration::from_secs(1)).await;
50///         Err(std::io::ErrorKind::TimedOut.into())
51///     })
52///     .await?;
53/// # std::io::Result::Ok(()) });
54/// ```
55pub struct Timer {
56    when: Option<Instant>,
57    period: Duration,
58    waker: Option<Waker>,
59}
60
61impl Timer {
62    /// Creates a timer that will never fire.
63    ///
64    /// # Examples
65    ///
66    /// This function may also be useful for creating a function with an optional timeout.
67    ///
68    /// ```
69    /// # futures_lite::future::block_on(async {
70    /// use async_io_mini::Timer;
71    /// use futures_lite::prelude::*;
72    /// use std::time::Duration;
73    ///
74    /// async fn run_with_timeout(timeout: Option<Duration>) {
75    ///     let timer = timeout
76    ///         .map(|timeout| Timer::after(timeout))
77    ///         .unwrap_or_else(Timer::never);
78    ///
79    ///     run_lengthy_operation().or(timer).await;
80    /// }
81    /// # // Note that since a Timer as a Future returns an Instant,
82    /// # // this function needs to return an Instant to be used
83    /// # // in "or".
84    /// # async fn run_lengthy_operation() -> std::time::Instant {
85    /// #    std::time::Instant::now()
86    /// # }
87    ///
88    /// // Times out after 5 seconds.
89    /// run_with_timeout(Some(Duration::from_secs(5))).await;
90    /// // Does not time out.
91    /// run_with_timeout(None).await;
92    /// # });
93    /// ```
94    pub fn never() -> Timer {
95        let _fix_linking = embassy_time::Timer::after(embassy_time::Duration::from_secs(1));
96
97        Timer {
98            when: None,
99            period: Duration::MAX,
100            waker: None,
101        }
102    }
103
104    /// Creates a timer that emits an event once after the given duration of time.
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// use async_io_mini::Timer;
110    /// use std::time::Duration;
111    ///
112    /// # futures_lite::future::block_on(async {
113    /// Timer::after(Duration::from_secs(1)).await;
114    /// # });
115    /// ```
116    pub fn after(duration: Duration) -> Timer {
117        let Some(start) = Instant::now().checked_add(duration) else {
118            return Timer::never();
119        };
120
121        Timer::interval_at(start, Duration::MAX)
122    }
123
124    /// Creates a timer that emits an event once at the given time instant.
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// use async_io_mini::Timer;
130    /// use std::time::{Duration, Instant};
131    ///
132    /// # futures_lite::future::block_on(async {
133    /// let now = Instant::now();
134    /// let when = now + Duration::from_secs(1);
135    /// Timer::at(when).await;
136    /// # });
137    /// ```
138    pub fn at(instant: Instant) -> Timer {
139        Timer::interval_at(instant, Duration::MAX)
140    }
141
142    /// Creates a timer that emits events periodically.
143    ///
144    /// # Examples
145    ///
146    /// ```
147    /// use async_io_mini::Timer;
148    /// use futures_lite::StreamExt;
149    /// use std::time::{Duration, Instant};
150    ///
151    /// # futures_lite::future::block_on(async {
152    /// let period = Duration::from_secs(1);
153    /// Timer::interval(period).next().await;
154    /// # });
155    /// ```
156    pub fn interval(period: Duration) -> Timer {
157        let Some(start) = Instant::now().checked_add(period) else {
158            return Timer::never();
159        };
160
161        Timer::interval_at(start, period)
162    }
163
164    /// Creates a timer that emits events periodically, starting at `start`.
165    ///
166    /// # Examples
167    ///
168    /// ```
169    /// use async_io_mini::Timer;
170    /// use futures_lite::StreamExt;
171    /// use std::time::{Duration, Instant};
172    ///
173    /// # futures_lite::future::block_on(async {
174    /// let start = Instant::now();
175    /// let period = Duration::from_secs(1);
176    /// Timer::interval_at(start, period).next().await;
177    /// # });
178    /// ```
179    pub fn interval_at(start: Instant, period: Duration) -> Timer {
180        if Self::ticks(&start).is_some() {
181            Timer {
182                when: Some(start),
183                period,
184                waker: None,
185            }
186        } else {
187            Timer::never()
188        }
189    }
190
191    /// Indicates whether or not this timer will ever fire.
192    ///
193    /// [`never()`] will never fire, and timers created with [`after()`] or [`at()`] will fire
194    /// if the duration is not too large.
195    ///
196    /// [`never()`]: Timer::never()
197    /// [`after()`]: Timer::after()
198    /// [`at()`]: Timer::at()
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// # futures_lite::future::block_on(async {
204    /// use async_io_mini::Timer;
205    /// use futures_lite::prelude::*;
206    /// use std::time::Duration;
207    ///
208    /// // `never` will never fire.
209    /// assert!(!Timer::never().will_fire());
210    ///
211    /// // `after` will fire if the duration is not too large.
212    /// assert!(Timer::after(Duration::from_secs(1)).will_fire());
213    /// assert!(!Timer::after(Duration::MAX).will_fire());
214    ///
215    /// // However, once an `after` timer has fired, it will never fire again.
216    /// let mut t = Timer::after(Duration::from_secs(1));
217    /// assert!(t.will_fire());
218    /// (&mut t).await;
219    /// assert!(!t.will_fire());
220    ///
221    /// // Interval timers will fire periodically.
222    /// let mut t = Timer::interval(Duration::from_secs(1));
223    /// assert!(t.will_fire());
224    /// t.next().await;
225    /// assert!(t.will_fire());
226    /// # });
227    /// ```
228    #[inline]
229    pub fn will_fire(&self) -> bool {
230        self.when.is_some()
231    }
232
233    /// Sets the timer to emit an en event once after the given duration of time.
234    ///
235    /// Note that resetting a timer is different from creating a new timer because
236    /// [`set_after()`][`Timer::set_after()`] does not remove the waker associated with the task
237    /// that is polling the timer.
238    ///
239    /// # Examples
240    ///
241    /// ```
242    /// use async_io_mini::Timer;
243    /// use std::time::Duration;
244    ///
245    /// # futures_lite::future::block_on(async {
246    /// let mut t = Timer::after(Duration::from_secs(1));
247    /// t.set_after(Duration::from_millis(100));
248    /// # });
249    /// ```
250    pub fn set_after(&mut self, duration: Duration) {
251        match Instant::now().checked_add(duration) {
252            Some(instant) => self.set_at(instant),
253            // Overflow to never going off.
254            None => self.set_never(),
255        }
256    }
257
258    /// Sets the timer to emit an event once at the given time instant.
259    ///
260    /// Note that resetting a timer is different from creating a new timer because
261    /// [`set_at()`][`Timer::set_at()`] does not remove the waker associated with the task
262    /// that is polling the timer.
263    ///
264    /// # Examples
265    ///
266    /// ```
267    /// use async_io_mini::Timer;
268    /// use std::time::{Duration, Instant};
269    ///
270    /// # futures_lite::future::block_on(async {
271    /// let mut t = Timer::after(Duration::from_secs(1));
272    ///
273    /// let now = Instant::now();
274    /// let when = now + Duration::from_secs(1);
275    /// t.set_at(when);
276    /// # });
277    /// ```
278    pub fn set_at(&mut self, instant: Instant) {
279        let ticks = Self::ticks(&instant);
280
281        if let Some(ticks) = ticks {
282            self.when = Some(instant);
283            self.period = Duration::MAX;
284
285            if let Some(waker) = self.waker.as_ref() {
286                embassy_time_driver::schedule_wake(ticks, waker);
287            }
288        } else {
289            self.set_never();
290        }
291    }
292
293    /// Sets the timer to emit events periodically.
294    ///
295    /// Note that resetting a timer is different from creating a new timer because
296    /// [`set_interval()`][`Timer::set_interval()`] does not remove the waker associated with the
297    /// task that is polling the timer.
298    ///
299    /// # Examples
300    ///
301    /// ```
302    /// use async_io_mini::Timer;
303    /// use futures_lite::StreamExt;
304    /// use std::time::{Duration, Instant};
305    ///
306    /// # futures_lite::future::block_on(async {
307    /// let mut t = Timer::after(Duration::from_secs(1));
308    ///
309    /// let period = Duration::from_secs(2);
310    /// t.set_interval(period);
311    /// # });
312    /// ```
313    pub fn set_interval(&mut self, period: Duration) {
314        match Instant::now().checked_add(period) {
315            Some(instant) => self.set_interval_at(instant, period),
316            // Overflow to never going off.
317            None => self.set_never(),
318        }
319    }
320
321    /// Sets the timer to emit events periodically, starting at `start`.
322    ///
323    /// Note that resetting a timer is different from creating a new timer because
324    /// [`set_interval_at()`][`Timer::set_interval_at()`] does not remove the waker associated with
325    /// the task that is polling the timer.
326    ///
327    /// # Examples
328    ///
329    /// ```
330    /// use async_io_mini::Timer;
331    /// use futures_lite::StreamExt;
332    /// use std::time::{Duration, Instant};
333    ///
334    /// # futures_lite::future::block_on(async {
335    /// let mut t = Timer::after(Duration::from_secs(1));
336    ///
337    /// let start = Instant::now();
338    /// let period = Duration::from_secs(2);
339    /// t.set_interval_at(start, period);
340    /// # });
341    /// ```
342    pub fn set_interval_at(&mut self, start: Instant, period: Duration) {
343        let ticks = Self::ticks(&start);
344
345        if let Some(ticks) = ticks {
346            self.when = Some(start);
347            self.period = period;
348
349            if let Some(waker) = self.waker.as_ref() {
350                embassy_time_driver::schedule_wake(ticks, waker);
351            }
352        } else {
353            // Overflow to never going off.
354            self.set_never();
355        }
356    }
357
358    fn set_never(&mut self) {
359        self.when = None;
360        self.waker = None;
361        self.period = Duration::MAX;
362    }
363
364    fn fired_at(&mut self, cx: &mut Context<'_>) -> Option<Instant> {
365        let when = self.when?;
366
367        if when > Instant::now() {
368            let ticks = Self::ticks(&when);
369
370            if let Some(ticks) = ticks {
371                if self
372                    .waker
373                    .as_ref()
374                    .map(|waker| !waker.will_wake(cx.waker()))
375                    .unwrap_or(true)
376                {
377                    self.waker = Some(cx.waker().clone());
378                    embassy_time_driver::schedule_wake(ticks, cx.waker());
379                }
380            } else {
381                self.set_never();
382            }
383
384            None
385        } else {
386            Some(when)
387        }
388    }
389
390    fn ticks(instant: &Instant) -> Option<u64> {
391        fn duration_ticks(duration: &Duration) -> Option<u64> {
392            let ticks = duration.as_secs() as u128 * embassy_time_driver::TICK_HZ as u128
393                + duration.subsec_nanos() as u128 * embassy_time_driver::TICK_HZ as u128
394                    / 1_000_000_000;
395
396            u64::try_from(ticks).ok()
397        }
398
399        let now = Instant::now();
400        let now_ticks = embassy_time_driver::now();
401
402        if *instant >= now {
403            let dur_ticks = duration_ticks(&instant.duration_since(now));
404
405            dur_ticks.and_then(|dur_ticks| now_ticks.checked_add(dur_ticks))
406        } else {
407            let dur_ticks = duration_ticks(&now.duration_since(*instant));
408
409            dur_ticks.map(|dur_ticks| now_ticks.saturating_sub(dur_ticks))
410        }
411    }
412}
413
414impl Debug for Timer {
415    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416        f.debug_struct("Timer")
417            .field("start", &self.when.as_ref())
418            .field("period", &self.period)
419            .finish()
420    }
421}
422
423impl Future for Timer {
424    type Output = Instant;
425
426    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
427        let Some(when) = self.fired_at(cx) else {
428            return Poll::Pending;
429        };
430
431        self.set_never();
432
433        Poll::Ready(when)
434    }
435}
436
437#[cfg(feature = "futures-lite")]
438impl futures_lite::Stream for Timer {
439    type Item = Instant;
440
441    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
442        let Some(when) = self.fired_at(cx) else {
443            return Poll::Pending;
444        };
445
446        let next_when = when.checked_add(self.period);
447
448        if let Some(next_when) = next_when {
449            let period = self.period;
450
451            self.set_interval_at(next_when, period);
452        } else {
453            self.set_never();
454        }
455
456        Poll::Ready(Some(when))
457    }
458}