async_hal/
executor.rs

1use core::{
2    cell::RefCell,
3    pin::Pin,
4    task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
5};
6use futures::Future;
7use once_cell::unsync::OnceCell;
8
9pub trait Interrupt {
10    fn pend(&self);
11}
12
13/// Task executor for a single `'static` future.
14///
15/// This provides a polling interface for a [`Future`] running on an interrupt handler.
16/// If the provided [`Waker`] is woken, the executor will pend the provided interrupt with [`Interrupt::pend`].
17///
18/// Executors must be static for use with the waker and [`Pin`] support.
19/// ```
20/// use async_hal::Executor;
21///
22/// static mut EXECUTOR: Executor<(), ()> = Executor::new(());
23/// ```
24#[cfg_attr(docsrs, doc(cfg(feature = "executor")))]
25pub struct Executor<I, F> {
26    interrupt: I,
27    future: OnceCell<RefCell<F>>,
28}
29
30impl<I, F> Executor<I, F> {
31    /// Create a new empty executor.
32    pub const fn new(interrupt: I) -> Self {
33        Self {
34            future: OnceCell::new(),
35            interrupt,
36        }
37    }
38
39    /// Spawn a single [`Future`] on the executor.
40    /// This method returns Ok(()) if the executor was empty and Err(value) if it was full.
41    pub fn spawn(&self, future: F) -> Result<(), F> {
42        self.future
43            .set(RefCell::new(future))
44            .map_err(|cell| cell.into_inner())
45    }
46
47    /// Poll the current [`Future`] on the executor.
48    pub fn poll(&'static self) -> Poll<F::Output>
49    where
50        I: Interrupt,
51        F: Future,
52    {
53        static VTABLE: RawWakerVTable = RawWakerVTable::new(
54            |ptr| RawWaker::new(ptr, &VTABLE),
55            |ptr| {
56                let me = unsafe { *ptr.cast::<&dyn Interrupt>() };
57                me.pend();
58            },
59            |ptr| {
60                let me = unsafe { *ptr.cast::<&dyn Interrupt>() };
61                me.pend();
62            },
63            |_| {},
64        );
65        let raw_waker = RawWaker::new(self as *const dyn Interrupt as *const (), &VTABLE);
66        let waker = unsafe { Waker::from_raw(raw_waker) };
67        let mut cx = Context::from_waker(&waker);
68
69        let mut future = self.future.get().unwrap().borrow_mut();
70
71        // Safety: `future` is guranteed to be static
72        let pinned = unsafe { Pin::new_unchecked(&mut *future) };
73        pinned.poll(&mut cx)
74    }
75}
76
77impl<I: Interrupt, F> Interrupt for Executor<I, F> {
78    fn pend(&self) {
79        self.interrupt.pend()
80    }
81}