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}