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}