embedded_runtime/executor/mod.rs
1//! The executor for this runtime
2
3mod waker;
4
5use crate::{err, error::Error, runtime};
6use core::{future::Future, pin::Pin, task::Context};
7
8/// A tiny stack-based, single-threaded async executor suitable for embedded runtimes
9///
10/// # Generics
11/// - `'a`: Tied to the lifetime of the pinned futures to execute (i.e. this executor must not outlive the futures it
12/// executes)
13/// - `T`: The type of futures to execute (e.g. `dyn Future<Output = ()>`)
14/// - `LEN`: The maximum amount of top-level futures this executor can execute (defaults to `32`)
15pub struct Executor<'a, T, const LEN: usize = 32>
16where
17 T: ?Sized,
18{
19 /// The registered futures
20 futures: [Option<Pin<&'a mut T>>; LEN],
21 /// The current length of `futures`
22 len: usize,
23}
24impl<'a, T, const LEN: usize> Executor<'a, T, LEN>
25where
26 T: Future<Output = ()> + ?Sized,
27{
28 /// Initialization value for const initializer
29 const INIT: Option<Pin<&'a mut T>> = None;
30
31 /// Creates a new executor
32 #[allow(clippy::new_without_default)]
33 pub fn new() -> Self {
34 Self { futures: [Self::INIT; LEN], len: 0 }
35 }
36
37 /// Registers a new future for execution
38 pub fn register(&mut self, future: Pin<&'a mut T>) -> Result<&mut Self, Error> {
39 // Get the next free slot
40 let Some(slot) = self.futures.get_mut(self.len) else {
41 return Err(err!("Executor has not enough space to register more futures"));
42 };
43
44 // Store the future
45 *slot = Some(future);
46 self.len += 1;
47 Ok(self)
48 }
49
50 /// Runs the executor
51 pub fn run(&mut self) {
52 // Create waker and context
53 let waker = waker::new();
54 let mut context = Context::from_waker(&waker);
55
56 // Repeatedly poll all futures until they have completed
57 let mut remaining = self.len;
58 while remaining > 0 {
59 // Poll each future
60 'poll_loop: for index in 0..self.len {
61 // Get the future if any
62 let Some(future) = &mut self.futures[index] else {
63 continue 'poll_loop;
64 };
65
66 // Poll the future
67 if future.as_mut().poll(&mut context).is_ready() {
68 // Unregister the future if it is dome
69 self.futures[index] = None;
70 remaining -= 1;
71 }
72 }
73
74 // Wait until at least one future signales that it is ready
75 // Note: We do this *after* the polling to ensure that each future is at least polled once
76 unsafe { runtime::_runtime_waitforevent_TBFzxdKN() };
77 }
78 }
79}
80
81/// Creates an executor and executes the given futures
82#[macro_export]
83macro_rules! run {
84 ($($futures:expr),+) => {{
85 /// Executes the given futures
86 let execute = || -> core::result::Result<(), $crate::error::Error> {
87 // Create executor
88 let mut executor: $crate::executor::Executor<'_, dyn core::future::Future<Output = ()>> =
89 $crate::executor::Executor::new();
90
91 // Register futures
92 $(
93 let future = core::pin::pin!($futures);
94 executor.register(future as core::pin::Pin<&mut dyn core::future::Future<Output = ()>>)?;
95 )*
96
97 // Execute the futures
98 executor.run();
99 Ok(())
100 };
101
102 // Execute all futures
103 execute()
104 }};
105}