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}