ndless_async/
task.rs

1//! Main task executing functionality
2//!
3//! The main ndless executor and reactor. Calling [`block_on`] will wait for the
4//! future to complete, then return its result. You'll often want to combine
5//! this with [`join`] or [`first`] to run multiple things at once.
6//!
7//! # Example
8//! ```
9//! use ndless_async::task::{AsyncListeners, block_on};
10//! use ndless_async::StreamExt;
11//! use ndless::input::Key;
12//! use ndless::prelude::*;
13//!
14//! let listeners = AsyncListeners::new();
15//! block_on(&listeners, listen(&listeners));
16//!
17//! async fn listen(listeners: &AsyncListeners) {
18//!     let mut keypad = listeners.keypad();
19//!     while let Some(event) = keypad.next().await {
20//!         println!("{:?}", key);
21//!         if event.key == Key::Esc {
22//!             break;
23//!         }
24//!     }
25//! }
26//! ```
27
28use alloc::{sync::Arc, task::Wake};
29use core::future::Future;
30use core::sync::atomic::{AtomicBool, Ordering};
31use core::task::{Context, Poll, Waker};
32
33use futures_util::pin_mut;
34use ndless::hw::idle;
35use ndless::timer::disable_sleep;
36
37use crate::timer::TimerListener;
38use crate::yield_now::{Yield, YieldListener};
39
40/// Spawns a task and blocks until the future resolves, returning its result.
41pub fn block_on<T>(listeners: &AsyncListeners, task: impl Future<Output = T>) -> T {
42	let wake_marker = Arc::new(TaskWaker {
43		wake_marker: AtomicBool::new(true),
44	});
45	let waker = Waker::from(wake_marker.clone());
46	let mut context = Context::from_waker(&waker);
47	pin_mut!(task);
48	let mut task = task;
49	loop {
50		listeners.timer.poll();
51		listeners.yielder.poll();
52		while wake_marker.wake_marker.load(Ordering::Relaxed) {
53			match task.as_mut().poll(&mut context) {
54				Poll::Ready(val) => {
55					disable_sleep();
56					return val;
57				}
58				Poll::Pending => {
59					wake_marker.wake_marker.store(false, Ordering::Relaxed);
60				}
61			}
62			listeners.timer.poll();
63			listeners.yielder.poll();
64		}
65		listeners.timer.config_sleep();
66		idle();
67		disable_sleep();
68	}
69}
70
71struct TaskWaker {
72	wake_marker: AtomicBool,
73}
74
75impl TaskWaker {
76	fn wake_task(&self) {
77		self.wake_marker.store(true, Ordering::Relaxed);
78	}
79}
80
81impl Wake for TaskWaker {
82	fn wake(self: Arc<Self>) {
83		self.wake_task();
84	}
85
86	fn wake_by_ref(self: &Arc<Self>) {
87		self.wake_task();
88	}
89}
90
91/// Handler for listening to system events.
92///
93/// Create one with `AsyncListeners::default()` and pass it to [`block_on`], as
94/// well as your future. See the [module-level documentation][self] for more.
95#[derive(Default)]
96pub struct AsyncListeners {
97	timer: TimerListener,
98	yielder: YieldListener,
99}
100
101impl AsyncListeners {
102	pub fn new() -> Self {
103		Default::default()
104	}
105	/// Returns a [`TimerListener`] instance, which may be used to schedule
106	/// timers.
107	pub fn timer(&self) -> &TimerListener {
108		&self.timer
109	}
110	/// Allows other tasks to run before coming back to this one. Useful when
111	/// doing something computationally intensive, to allow things like keyboard
112	/// handlers and timers to run. Note that the calculator will not go to
113	/// sleep if this is called in a loop. Use [`timer`][AsyncListeners::timer]
114	/// instead if a delay is desired between each iteration. If no other tasks
115	/// are scheduled, this task is continued immediately.
116	pub fn yield_now(&self) -> Yield {
117		self.yielder.yield_now()
118	}
119}