ndless_async/timer.rs
1//! Timers, Timeouts, and Intervals
2//!
3//! Waits for a specific time frame and then completes the `Future`. The
4//! calculator is automatically put to sleep until the next timer to conserve
5//! power.
6//!
7//! Check out [`TimerListener`]'s documentation for more.
8use alloc::rc::Rc;
9use core::cell::{Cell, RefCell};
10use core::fmt;
11use core::future::Future;
12use core::pin::Pin;
13
14use core::time::Duration;
15use futures_util::future::FutureExt;
16use futures_util::pin_mut;
17use futures_util::stream::Stream;
18use futures_util::task::{AtomicWaker, Context, Poll};
19use ndless::alloc::fmt::Formatter;
20use ndless::prelude::*;
21use ndless::timer::{configure_sleep, get_ticks, has_time_passed, Ticks, TICKS_PER_SECOND};
22
23use crate::select;
24
25struct TimerData {
26 at_tick: Cell<u32>,
27 waker: AtomicWaker,
28}
29
30/// Timer Listener
31///
32/// Used to create [`Timer`]s, which may be `.await`ed to wait for a specific
33/// time. See [`AsyncListeners`][crate::task::AsyncListeners] to get one.
34#[derive(Default)]
35pub struct TimerListener {
36 timers: RefCell<Vec<Rc<TimerData>>>,
37}
38
39impl TimerListener {
40 pub(crate) fn poll(&self) {
41 let mut timers = self.timers.borrow_mut();
42 timers.retain(|timer| Rc::strong_count(timer) > 1);
43 timers.iter().for_each(|timer| {
44 if has_time_passed(timer.at_tick.get()) {
45 timer.waker.wake();
46 }
47 })
48 }
49 pub(crate) fn config_sleep(&self) {
50 let half_max = 2u32.pow(31);
51 let mut timers = self.timers.borrow_mut();
52 timers.retain(|timer| Rc::strong_count(timer) > 1);
53 if let Some(time) = timers
54 .iter()
55 .map(|timer| timer.at_tick.get().wrapping_sub(get_ticks()) % half_max)
56 .min()
57 {
58 configure_sleep(time);
59 }
60 }
61 /// Sleeps for the specified number of milliseconds. Problems will occur
62 /// when sleeping for more than 2^31/32768 seconds, which is about 18 hours.
63 pub fn sleep_ms(&self, ms: u32) -> Timer {
64 self.sleep(Duration::from_millis(ms as u64))
65 }
66 /// Sleeps for the specified [`Duration`]. Problems will occur
67 /// when sleeping for more than 2^31/32768 seconds, which is about 18 hours.
68 ///
69 /// This function has a resolution of 30 μs.
70 pub fn sleep(&self, dur: Duration) -> Timer {
71 self.sleep_ticks(dur.as_ticks())
72 }
73 /// Sleeps for the specified number of
74 /// [ticks](https://docs.rs/ndless/0.8.*/ndless/timer/fn.get_ticks.html).
75 /// Problems will occur when sleeping for more than 2^31 ticks,
76 /// which is about 18 hours.
77 pub fn sleep_ticks(&self, ticks: u32) -> Timer {
78 self.sleep_until(get_ticks().wrapping_add(ticks))
79 }
80 /// Sleeps until the current number of ticks is equal to the parameter.
81 /// Problems will occur when sleeping for more than 2^31 ticks in the
82 /// future, which is about 18 hours.
83 pub fn sleep_until(&self, ticks: u32) -> Timer {
84 let timer = Rc::new(TimerData {
85 at_tick: Cell::new(ticks),
86 waker: AtomicWaker::new(),
87 });
88 let mut timers = self.timers.borrow_mut();
89 timers.push(timer.clone());
90 Timer(timer)
91 }
92 /// Awaits a future or times out after the specified number of milliseconds.
93 /// Problems will occur when sleeping for more than 2^31/32768 seconds,
94 /// which is about 18 hours.
95 pub async fn timeout_ms<T>(
96 &self,
97 ms: u32,
98 f: impl Future<Output = T>,
99 ) -> Result<T, TimeoutError> {
100 self.timeout(Duration::from_millis(ms as u64), f).await
101 }
102 /// Awaits a future or times out after the specified [`Duration`]. Problems
103 /// will occur when sleeping for more than 2^31/32768 seconds, which is
104 /// about 18 hours.
105 ///
106 /// This function has a resolution of 30 μs.
107 pub async fn timeout<T>(
108 &self,
109 dur: Duration,
110 f: impl Future<Output = T>,
111 ) -> Result<T, TimeoutError> {
112 self.timeout_ticks(dur.as_ticks(), f).await
113 }
114 /// Awaits a future or times out after the specified number of
115 /// [ticks](https://docs.rs/ndless/0.8.*/ndless/timer/fn.get_ticks.html).
116 /// Problems will occur when sleeping for more than 2^31 ticks,
117 /// which is about 18 hours.
118 pub async fn timeout_ticks<T>(
119 &self,
120 ticks: u32,
121 f: impl Future<Output = T>,
122 ) -> Result<T, TimeoutError> {
123 self.timeout_until(get_ticks().wrapping_add(ticks), f).await
124 }
125 /// Awaits a future or times out after the current number of ticks is equal
126 /// to the parameter. Problems will occur when sleeping for more than 2^31
127 /// ticks in the future, which is about 18 hours.
128 pub async fn timeout_until<T>(
129 &self,
130 ticks: u32,
131 f: impl Future<Output = T>,
132 ) -> Result<T, TimeoutError> {
133 let f = f.fuse();
134 pin_mut!(f);
135 select! {
136 x = f => Ok(x),
137 _ = self.sleep_until(ticks).fuse() => Err(TimeoutError),
138 }
139 }
140 /// Creates a [`Stream`] that triggers with the specified number of events
141 /// per second.
142 pub fn every_hz(&self, hz: u32) -> Interval {
143 self.every_ticks(TICKS_PER_SECOND / hz)
144 }
145 /// Creates a [`Stream`] that triggers every specified number of
146 /// milliseconds. Problems will occur when sleeping for more than 2^31/32768
147 /// seconds, which is about 18 hours.
148 pub fn every_ms(&self, ms: u32) -> Interval {
149 self.every(Duration::from_millis(ms as u64))
150 }
151 /// Creates a [`Stream`] that triggers every specified [`Duration`].
152 /// Problems will occur when sleeping for more than 2^31/32768 seconds,
153 /// which is about 18 hours.
154 ///
155 /// This function has a resolution of 30 μs.
156 pub fn every(&self, dur: Duration) -> Interval {
157 self.every_ticks(dur.as_ticks())
158 }
159 /// Creates a [`Stream`] that triggers every specified number of
160 /// [ticks](https://docs.rs/ndless/0.8.*/ndless/timer/fn.get_ticks.html).
161 /// Problems will occur when sleeping for more than 2^31 ticks, which is
162 /// about 18 hours.
163 pub fn every_ticks(&self, ticks: u32) -> Interval {
164 Interval {
165 interval: ticks,
166 timer: self.sleep_ticks(ticks),
167 }
168 }
169}
170
171/// A timer that keeps re-triggering.
172///
173/// Use [`TimerListener::every`], [`TimerListener::every_hz`],
174/// [`TimerListener::every_ms`], or [`TimerListener::every_ticks`] to get an
175/// `Interval`.
176///
177/// This implements [`Stream`], giving the [`Duration`] of time
178/// ago when this *should* have been triggered. If a task takes a lot of time,
179/// the [`Stream`] will only produce one event. This is likely what you want for
180/// handling events like keypad input, as only one event will trigger after
181/// blocking for a while, rather than many events being triggered right after
182/// the blocking event.
183///
184/// ```rust
185/// use ndless_async::StreamExt;
186/// use ndless_async::task::{block_on, AsyncListeners};
187///
188/// let listeners = AsyncListeners::new();
189/// block_on(&listeners, async {
190/// let mut interval = listeners.timer().every_ms(1000);
191/// while let Some(d) = interval.next().await {
192/// println!("Ping! This event was expected {:?} ago", d);
193/// }
194/// });
195/// ```
196pub struct Interval {
197 interval: u32,
198 timer: Timer,
199}
200
201impl Interval {
202 /// The interval that this `Interval` triggers
203 pub fn interval(&self) -> Duration {
204 Duration::from_ticks(self.interval)
205 }
206 /// The interval, in milliseconds, that this `Interval` triggers
207 pub fn interval_ms(&self) -> u32 {
208 self.interval().as_ticks()
209 }
210 /// The interval, in ticks, that this `Interval` triggers
211 pub fn interval_ticks(&self) -> u32 {
212 self.interval
213 }
214 /// Reschedules this interval for the specified number of milliseconds.
215 /// Problems will occur when sleeping for more than 2^31/32768 seconds,
216 /// which is about 18 hours.
217 pub fn reschedule_ms(&mut self, ms: u32) {
218 self.reschedule(Duration::from_millis(ms as u64))
219 }
220 /// Reschedules this interval for the specified [`Duration`]. Problems will
221 /// occur when sleeping for more than 2^31/32768 seconds, which is about 18
222 /// hours.
223 ///
224 /// This function has a resolution of 30 μs.
225 pub fn reschedule(&mut self, dur: Duration) {
226 self.reschedule_ticks(dur.as_ticks())
227 }
228 /// Reschedules this interval for the specified number of
229 /// [ticks](https://docs.rs/ndless/0.8.*/ndless/timer/fn.get_ticks.html).
230 /// Problems will occur when sleeping for more than 2^31 ticks, which is
231 /// about 18 hours.
232 pub fn reschedule_ticks(&mut self, ticks: u32) {
233 self.interval = ticks;
234 self.timer.reschedule_ticks(ticks)
235 }
236}
237
238impl Stream for Interval {
239 /// The difference between now and when this event *should* have occurred.
240 type Item = Duration;
241
242 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
243 let res = Pin::new(&mut self.timer).poll(cx);
244 match res {
245 Poll::Ready(dur) => {
246 self.timer.reschedule_ticks(self.interval);
247 Poll::Ready(Some(dur))
248 }
249 Poll::Pending => Poll::Pending,
250 }
251 }
252}
253
254/// Waits for a specific time.
255///
256/// Use [`TimerListener::sleep`], [`TimerListener::sleep_ms`],
257/// [`TimerListener::sleep_ticks`], or [`TimerListener::sleep_until`] to create
258/// a `Timer`.
259///
260/// The timer can be rescheduled with the `reschedule` series of functions. If
261/// the original time period has already passed, it will re-trigger after the
262/// new time period.
263///
264/// The calculator is automatically put to sleep until the next timer to
265/// conserve power.
266///
267/// ```rust
268/// use ndless_async::task::{block_on, AsyncListeners};
269///
270/// let listeners = AsyncListeners::new();
271/// block_on(&listeners, async {
272/// let late_by = listeners.timer().sleep_ms(1000).await;
273/// println!("Done sleeping! This event was expected {:?} ago", late_by);
274/// });
275/// ```
276pub struct Timer(Rc<TimerData>);
277
278impl Timer {
279 /// Get the tick that this timer should fire at
280 pub fn at_tick(&self) -> u32 {
281 self.0.at_tick.get()
282 }
283 /// Reschedules this timer for the specified number of milliseconds.
284 /// Problems will occur when sleeping for more than 2^31/32768 seconds,
285 /// which is about 18 hours.
286 ///
287 /// If this timer has already triggered, it will trigger again after the
288 /// specified delay.
289 pub fn reschedule_ms(&self, ms: u32) {
290 self.reschedule(Duration::from_millis(ms as u64))
291 }
292 /// Reschedules this timer for the specified [`Duration`]. Problems will
293 /// occur when sleeping for more than 2^31/32768 seconds, which is about 18
294 /// hours.
295 ///
296 /// If this timer has already triggered, it will trigger again after the
297 /// specified delay.
298 ///
299 /// This function has a resolution of 30 μs.
300 pub fn reschedule(&self, dur: Duration) {
301 self.reschedule_ticks(dur.as_ticks())
302 }
303 /// Reschedules this timer for the specified number of
304 /// [ticks](https://docs.rs/ndless/0.8.*/ndless/timer/fn.get_ticks.html).
305 /// Problems will occur when sleeping for more than 2^31 ticks, which is
306 /// about 18 hours.
307 ///
308 /// If this timer has already triggered, it will trigger again after the
309 /// specified delay.
310 pub fn reschedule_ticks(&self, ticks: u32) {
311 self.reschedule_at(get_ticks().wrapping_add(ticks))
312 }
313 /// Reschedules this timer until the current number of ticks is equal to the
314 /// parameter. Problems will occur when sleeping for more than 2^31 ticks in
315 /// the future, which is about 18 hours.
316 ///
317 /// If this timer has already triggered, it will trigger again after the
318 /// specified delay.
319 pub fn reschedule_at(&self, ticks: u32) {
320 self.0.at_tick.set(ticks);
321 }
322}
323
324impl Future for Timer {
325 /// The difference between now and when this event *should* have occurred.
326 type Output = Duration;
327
328 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
329 let at_tick = self.at_tick();
330 if has_time_passed(at_tick) {
331 Poll::Ready(Duration::from_ticks(get_ticks().wrapping_sub(at_tick)))
332 } else {
333 self.0.waker.register(cx.waker());
334 Poll::Pending
335 }
336 }
337}
338
339/// An error returned when a future times out.
340///
341/// This may occur when using [`TimerListener::timeout`],
342/// [`TimerListener::timeout_ms`], [`TimerListener::timeout_ticks`], or
343/// [`TimerListener::timeout_until`].
344#[derive(Eq, PartialEq, Copy, Clone, Default, Debug, Hash)]
345pub struct TimeoutError;
346
347impl fmt::Display for TimeoutError {
348 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
349 "future has timed out".fmt(f)
350 }
351}