Merge pull request #1774 from bugadani/executor
Allow custom executors without indirection
This commit is contained in:
		@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
 | 
				
			|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 | 
					The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 | 
				
			||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 | 
					and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Unreleased
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Replaced Pender. Implementations now must define an extern function called `__pender`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 0.2.1 - 2023-08-10
 | 
					## 0.2.1 - 2023-08-10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Avoid calling `pend()` when waking expired timers
 | 
					- Avoid calling `pend()` when waking expired timers
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@ categories = [
 | 
				
			|||||||
[package.metadata.embassy_docs]
 | 
					[package.metadata.embassy_docs]
 | 
				
			||||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
 | 
					src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
 | 
				
			||||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
 | 
					src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
 | 
				
			||||||
features = ["nightly", "defmt", "pender-callback"]
 | 
					features = ["nightly", "defmt"]
 | 
				
			||||||
flavors = [
 | 
					flavors = [
 | 
				
			||||||
    { name = "std",             target = "x86_64-unknown-linux-gnu",     features = ["arch-std", "executor-thread"] },
 | 
					    { name = "std",             target = "x86_64-unknown-linux-gnu",     features = ["arch-std", "executor-thread"] },
 | 
				
			||||||
    { name = "wasm",            target = "wasm32-unknown-unknown",       features = ["arch-wasm", "executor-thread"] },
 | 
					    { name = "wasm",            target = "wasm32-unknown-unknown",       features = ["arch-wasm", "executor-thread"] },
 | 
				
			||||||
@@ -25,7 +25,7 @@ flavors = [
 | 
				
			|||||||
[package.metadata.docs.rs]
 | 
					[package.metadata.docs.rs]
 | 
				
			||||||
default-target = "thumbv7em-none-eabi"
 | 
					default-target = "thumbv7em-none-eabi"
 | 
				
			||||||
targets = ["thumbv7em-none-eabi"]
 | 
					targets = ["thumbv7em-none-eabi"]
 | 
				
			||||||
features = ["nightly", "defmt", "pender-callback", "arch-cortex-m", "executor-thread", "executor-interrupt"]
 | 
					features = ["nightly", "defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[features]
 | 
					[features]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -37,9 +37,6 @@ arch-xtensa = ["_arch"]
 | 
				
			|||||||
arch-riscv32 = ["_arch"]
 | 
					arch-riscv32 = ["_arch"]
 | 
				
			||||||
arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
 | 
					arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Enable creating a `Pender` from an arbitrary function pointer callback.
 | 
					 | 
				
			||||||
pender-callback = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
 | 
					# Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
 | 
				
			||||||
executor-thread = []
 | 
					executor-thread = []
 | 
				
			||||||
# Enable the interrupt-mode executor (available in Cortex-M only)
 | 
					# Enable the interrupt-mode executor (available in Cortex-M only)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,49 @@
 | 
				
			|||||||
 | 
					const THREAD_PENDER: usize = usize::MAX;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[export_name = "__pender"]
 | 
				
			||||||
 | 
					#[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))]
 | 
				
			||||||
 | 
					fn __pender(context: *mut ()) {
 | 
				
			||||||
 | 
					    unsafe {
 | 
				
			||||||
 | 
					        // Safety: `context` is either `usize::MAX` created by `Executor::run`, or a valid interrupt
 | 
				
			||||||
 | 
					        // request number given to `InterruptExecutor::start`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let context = context as usize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "executor-thread")]
 | 
				
			||||||
 | 
					        // Try to make Rust optimize the branching away if we only use thread mode.
 | 
				
			||||||
 | 
					        if !cfg!(feature = "executor-interrupt") || context == THREAD_PENDER {
 | 
				
			||||||
 | 
					            core::arch::asm!("sev");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #[cfg(feature = "executor-interrupt")]
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            use cortex_m::interrupt::InterruptNumber;
 | 
				
			||||||
 | 
					            use cortex_m::peripheral::NVIC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            #[derive(Clone, Copy)]
 | 
				
			||||||
 | 
					            struct Irq(u16);
 | 
				
			||||||
 | 
					            unsafe impl InterruptNumber for Irq {
 | 
				
			||||||
 | 
					                fn number(self) -> u16 {
 | 
				
			||||||
 | 
					                    self.0
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let irq = Irq(context as u16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // STIR is faster, but is only available in v7 and higher.
 | 
				
			||||||
 | 
					            #[cfg(not(armv6m))]
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                let mut nvic: NVIC = core::mem::transmute(());
 | 
				
			||||||
 | 
					                nvic.request(irq);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            #[cfg(armv6m)]
 | 
				
			||||||
 | 
					            NVIC::pend(irq);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(feature = "executor-thread")]
 | 
					#[cfg(feature = "executor-thread")]
 | 
				
			||||||
pub use thread::*;
 | 
					pub use thread::*;
 | 
				
			||||||
#[cfg(feature = "executor-thread")]
 | 
					#[cfg(feature = "executor-thread")]
 | 
				
			||||||
@@ -8,18 +54,9 @@ mod thread {
 | 
				
			|||||||
    #[cfg(feature = "nightly")]
 | 
					    #[cfg(feature = "nightly")]
 | 
				
			||||||
    pub use embassy_macros::main_cortex_m as main;
 | 
					    pub use embassy_macros::main_cortex_m as main;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use crate::raw::{Pender, PenderInner};
 | 
					    use crate::arch::THREAD_PENDER;
 | 
				
			||||||
    use crate::{raw, Spawner};
 | 
					    use crate::{raw, Spawner};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[derive(Copy, Clone)]
 | 
					 | 
				
			||||||
    pub(crate) struct ThreadPender;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl ThreadPender {
 | 
					 | 
				
			||||||
        pub(crate) fn pend(self) {
 | 
					 | 
				
			||||||
            unsafe { core::arch::asm!("sev") }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Thread mode executor, using WFE/SEV.
 | 
					    /// Thread mode executor, using WFE/SEV.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// This is the simplest and most common kind of executor. It runs on
 | 
					    /// This is the simplest and most common kind of executor. It runs on
 | 
				
			||||||
@@ -39,7 +76,7 @@ mod thread {
 | 
				
			|||||||
        /// Create a new Executor.
 | 
					        /// Create a new Executor.
 | 
				
			||||||
        pub fn new() -> Self {
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
            Self {
 | 
					            Self {
 | 
				
			||||||
                inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
 | 
					                inner: raw::Executor::new(THREAD_PENDER as *mut ()),
 | 
				
			||||||
                not_send: PhantomData,
 | 
					                not_send: PhantomData,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -86,30 +123,7 @@ mod interrupt {
 | 
				
			|||||||
    use cortex_m::interrupt::InterruptNumber;
 | 
					    use cortex_m::interrupt::InterruptNumber;
 | 
				
			||||||
    use cortex_m::peripheral::NVIC;
 | 
					    use cortex_m::peripheral::NVIC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use crate::raw::{self, Pender, PenderInner};
 | 
					    use crate::raw;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    #[derive(Clone, Copy)]
 | 
					 | 
				
			||||||
    pub(crate) struct InterruptPender(u16);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl InterruptPender {
 | 
					 | 
				
			||||||
        pub(crate) fn pend(self) {
 | 
					 | 
				
			||||||
            // STIR is faster, but is only available in v7 and higher.
 | 
					 | 
				
			||||||
            #[cfg(not(armv6m))]
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) };
 | 
					 | 
				
			||||||
                nvic.request(self);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            #[cfg(armv6m)]
 | 
					 | 
				
			||||||
            cortex_m::peripheral::NVIC::pend(self);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender {
 | 
					 | 
				
			||||||
        fn number(self) -> u16 {
 | 
					 | 
				
			||||||
            self.0
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Interrupt mode executor.
 | 
					    /// Interrupt mode executor.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
@@ -194,9 +208,7 @@ mod interrupt {
 | 
				
			|||||||
            unsafe {
 | 
					            unsafe {
 | 
				
			||||||
                (&mut *self.executor.get())
 | 
					                (&mut *self.executor.get())
 | 
				
			||||||
                    .as_mut_ptr()
 | 
					                    .as_mut_ptr()
 | 
				
			||||||
                    .write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender(
 | 
					                    .write(raw::Executor::new(irq.number() as *mut ()))
 | 
				
			||||||
                        irq.number(),
 | 
					 | 
				
			||||||
                    )))))
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
 | 
					            let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,22 +11,16 @@ mod thread {
 | 
				
			|||||||
    #[cfg(feature = "nightly")]
 | 
					    #[cfg(feature = "nightly")]
 | 
				
			||||||
    pub use embassy_macros::main_riscv as main;
 | 
					    pub use embassy_macros::main_riscv as main;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use crate::raw::{Pender, PenderInner};
 | 
					 | 
				
			||||||
    use crate::{raw, Spawner};
 | 
					    use crate::{raw, Spawner};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[derive(Copy, Clone)]
 | 
					 | 
				
			||||||
    pub(crate) struct ThreadPender;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl ThreadPender {
 | 
					 | 
				
			||||||
        #[allow(unused)]
 | 
					 | 
				
			||||||
        pub(crate) fn pend(self) {
 | 
					 | 
				
			||||||
            SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV
 | 
					    /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV
 | 
				
			||||||
    static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
 | 
					    static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[export_name = "__pender"]
 | 
				
			||||||
 | 
					    fn __pender(_context: *mut ()) {
 | 
				
			||||||
 | 
					        SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// RISCV32 Executor
 | 
					    /// RISCV32 Executor
 | 
				
			||||||
    pub struct Executor {
 | 
					    pub struct Executor {
 | 
				
			||||||
        inner: raw::Executor,
 | 
					        inner: raw::Executor,
 | 
				
			||||||
@@ -37,7 +31,7 @@ mod thread {
 | 
				
			|||||||
        /// Create a new Executor.
 | 
					        /// Create a new Executor.
 | 
				
			||||||
        pub fn new() -> Self {
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
            Self {
 | 
					            Self {
 | 
				
			||||||
                inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
 | 
					                inner: raw::Executor::new(core::ptr::null_mut()),
 | 
				
			||||||
                not_send: PhantomData,
 | 
					                not_send: PhantomData,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,17 +11,12 @@ mod thread {
 | 
				
			|||||||
    #[cfg(feature = "nightly")]
 | 
					    #[cfg(feature = "nightly")]
 | 
				
			||||||
    pub use embassy_macros::main_std as main;
 | 
					    pub use embassy_macros::main_std as main;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use crate::raw::{Pender, PenderInner};
 | 
					 | 
				
			||||||
    use crate::{raw, Spawner};
 | 
					    use crate::{raw, Spawner};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[derive(Copy, Clone)]
 | 
					    #[export_name = "__pender"]
 | 
				
			||||||
    pub(crate) struct ThreadPender(&'static Signaler);
 | 
					    fn __pender(context: *mut ()) {
 | 
				
			||||||
 | 
					        let signaler: &'static Signaler = unsafe { std::mem::transmute(context) };
 | 
				
			||||||
    impl ThreadPender {
 | 
					        signaler.signal()
 | 
				
			||||||
        #[allow(unused)]
 | 
					 | 
				
			||||||
        pub(crate) fn pend(self) {
 | 
					 | 
				
			||||||
            self.0.signal()
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Single-threaded std-based executor.
 | 
					    /// Single-threaded std-based executor.
 | 
				
			||||||
@@ -34,9 +29,9 @@ mod thread {
 | 
				
			|||||||
    impl Executor {
 | 
					    impl Executor {
 | 
				
			||||||
        /// Create a new Executor.
 | 
					        /// Create a new Executor.
 | 
				
			||||||
        pub fn new() -> Self {
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
            let signaler = &*Box::leak(Box::new(Signaler::new()));
 | 
					            let signaler = Box::leak(Box::new(Signaler::new()));
 | 
				
			||||||
            Self {
 | 
					            Self {
 | 
				
			||||||
                inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))),
 | 
					                inner: raw::Executor::new(signaler as *mut Signaler as *mut ()),
 | 
				
			||||||
                not_send: PhantomData,
 | 
					                not_send: PhantomData,
 | 
				
			||||||
                signaler,
 | 
					                signaler,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,14 +14,12 @@ mod thread {
 | 
				
			|||||||
    use wasm_bindgen::prelude::*;
 | 
					    use wasm_bindgen::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use crate::raw::util::UninitCell;
 | 
					    use crate::raw::util::UninitCell;
 | 
				
			||||||
    use crate::raw::{Pender, PenderInner};
 | 
					 | 
				
			||||||
    use crate::{raw, Spawner};
 | 
					    use crate::{raw, Spawner};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
 | 
					    #[export_name = "__pender"]
 | 
				
			||||||
    pub struct Executor {
 | 
					    fn __pender(context: *mut ()) {
 | 
				
			||||||
        inner: raw::Executor,
 | 
					        let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) };
 | 
				
			||||||
        ctx: &'static WasmContext,
 | 
					        let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() });
 | 
				
			||||||
        not_send: PhantomData<*mut ()>,
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(crate) struct WasmContext {
 | 
					    pub(crate) struct WasmContext {
 | 
				
			||||||
@@ -29,16 +27,6 @@ mod thread {
 | 
				
			|||||||
        closure: UninitCell<Closure<dyn FnMut(JsValue)>>,
 | 
					        closure: UninitCell<Closure<dyn FnMut(JsValue)>>,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[derive(Copy, Clone)]
 | 
					 | 
				
			||||||
    pub(crate) struct ThreadPender(&'static WasmContext);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl ThreadPender {
 | 
					 | 
				
			||||||
        #[allow(unused)]
 | 
					 | 
				
			||||||
        pub(crate) fn pend(self) {
 | 
					 | 
				
			||||||
            let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl WasmContext {
 | 
					    impl WasmContext {
 | 
				
			||||||
        pub fn new() -> Self {
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
            Self {
 | 
					            Self {
 | 
				
			||||||
@@ -48,14 +36,21 @@ mod thread {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop.
 | 
				
			||||||
 | 
					    pub struct Executor {
 | 
				
			||||||
 | 
					        inner: raw::Executor,
 | 
				
			||||||
 | 
					        ctx: &'static WasmContext,
 | 
				
			||||||
 | 
					        not_send: PhantomData<*mut ()>,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    impl Executor {
 | 
					    impl Executor {
 | 
				
			||||||
        /// Create a new Executor.
 | 
					        /// Create a new Executor.
 | 
				
			||||||
        pub fn new() -> Self {
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
            let ctx = &*Box::leak(Box::new(WasmContext::new()));
 | 
					            let ctx = Box::leak(Box::new(WasmContext::new()));
 | 
				
			||||||
            Self {
 | 
					            Self {
 | 
				
			||||||
                inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))),
 | 
					                inner: raw::Executor::new(ctx as *mut WasmContext as *mut ()),
 | 
				
			||||||
                not_send: PhantomData,
 | 
					 | 
				
			||||||
                ctx,
 | 
					                ctx,
 | 
				
			||||||
 | 
					                not_send: PhantomData,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,22 +8,16 @@ mod thread {
 | 
				
			|||||||
    use core::marker::PhantomData;
 | 
					    use core::marker::PhantomData;
 | 
				
			||||||
    use core::sync::atomic::{AtomicBool, Ordering};
 | 
					    use core::sync::atomic::{AtomicBool, Ordering};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use crate::raw::{Pender, PenderInner};
 | 
					 | 
				
			||||||
    use crate::{raw, Spawner};
 | 
					    use crate::{raw, Spawner};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[derive(Copy, Clone)]
 | 
					 | 
				
			||||||
    pub(crate) struct ThreadPender;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    impl ThreadPender {
 | 
					 | 
				
			||||||
        #[allow(unused)]
 | 
					 | 
				
			||||||
        pub(crate) fn pend(self) {
 | 
					 | 
				
			||||||
            SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
 | 
					    /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa
 | 
				
			||||||
    static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
 | 
					    static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    #[export_name = "__pender"]
 | 
				
			||||||
 | 
					    fn __pender(_context: *mut ()) {
 | 
				
			||||||
 | 
					        SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Xtensa Executor
 | 
					    /// Xtensa Executor
 | 
				
			||||||
    pub struct Executor {
 | 
					    pub struct Executor {
 | 
				
			||||||
        inner: raw::Executor,
 | 
					        inner: raw::Executor,
 | 
				
			||||||
@@ -34,7 +28,7 @@ mod thread {
 | 
				
			|||||||
        /// Create a new Executor.
 | 
					        /// Create a new Executor.
 | 
				
			||||||
        pub fn new() -> Self {
 | 
					        pub fn new() -> Self {
 | 
				
			||||||
            Self {
 | 
					            Self {
 | 
				
			||||||
                inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))),
 | 
					                inner: raw::Executor::new(core::ptr::null_mut()),
 | 
				
			||||||
                not_send: PhantomData,
 | 
					                not_send: PhantomData,
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -77,8 +71,8 @@ mod thread {
 | 
				
			|||||||
                        SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
 | 
					                        SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        core::arch::asm!(
 | 
					                        core::arch::asm!(
 | 
				
			||||||
                            "wsr.ps {0}",
 | 
					                        "wsr.ps {0}",
 | 
				
			||||||
                            "rsync", in(reg) token)
 | 
					                        "rsync", in(reg) token)
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        // waiti sets the PS.INTLEVEL when slipping into sleep
 | 
					                        // waiti sets the PS.INTLEVEL when slipping into sleep
 | 
				
			||||||
                        // because critical sections in Xtensa are implemented via increasing
 | 
					                        // because critical sections in Xtensa are implemented via increasing
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -292,53 +292,17 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone, Copy)]
 | 
					#[derive(Clone, Copy)]
 | 
				
			||||||
pub(crate) enum PenderInner {
 | 
					pub(crate) struct Pender(*mut ());
 | 
				
			||||||
    #[cfg(feature = "executor-thread")]
 | 
					 | 
				
			||||||
    Thread(crate::arch::ThreadPender),
 | 
					 | 
				
			||||||
    #[cfg(feature = "executor-interrupt")]
 | 
					 | 
				
			||||||
    Interrupt(crate::arch::InterruptPender),
 | 
					 | 
				
			||||||
    #[cfg(feature = "pender-callback")]
 | 
					 | 
				
			||||||
    Callback { func: fn(*mut ()), context: *mut () },
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
unsafe impl Send for PenderInner {}
 | 
					unsafe impl Send for Pender {}
 | 
				
			||||||
unsafe impl Sync for PenderInner {}
 | 
					unsafe impl Sync for Pender {}
 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Platform/architecture-specific action executed when an executor has pending work.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// When a task within an executor is woken, the `Pender` is called. This does a
 | 
					 | 
				
			||||||
/// platform/architecture-specific action to signal there is pending work in the executor.
 | 
					 | 
				
			||||||
/// When this happens, you must arrange for [`Executor::poll`] to be called.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// You can think of it as a waker, but for the whole executor.
 | 
					 | 
				
			||||||
pub struct Pender(pub(crate) PenderInner);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Pender {
 | 
					impl Pender {
 | 
				
			||||||
    /// Create a `Pender` that will call an arbitrary function pointer.
 | 
					    pub(crate) fn pend(self) {
 | 
				
			||||||
    ///
 | 
					        extern "Rust" {
 | 
				
			||||||
    /// # Arguments
 | 
					            fn __pender(context: *mut ());
 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// - `func`: The function pointer to call.
 | 
					 | 
				
			||||||
    /// - `context`: Opaque context pointer, that will be passed to the function pointer.
 | 
					 | 
				
			||||||
    #[cfg(feature = "pender-callback")]
 | 
					 | 
				
			||||||
    pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self {
 | 
					 | 
				
			||||||
        Self(PenderInner::Callback {
 | 
					 | 
				
			||||||
            func,
 | 
					 | 
				
			||||||
            context: context.into(),
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Pender {
 | 
					 | 
				
			||||||
    pub(crate) fn pend(&self) {
 | 
					 | 
				
			||||||
        match self.0 {
 | 
					 | 
				
			||||||
            #[cfg(feature = "executor-thread")]
 | 
					 | 
				
			||||||
            PenderInner::Thread(x) => x.pend(),
 | 
					 | 
				
			||||||
            #[cfg(feature = "executor-interrupt")]
 | 
					 | 
				
			||||||
            PenderInner::Interrupt(x) => x.pend(),
 | 
					 | 
				
			||||||
            #[cfg(feature = "pender-callback")]
 | 
					 | 
				
			||||||
            PenderInner::Callback { func, context } => func(context),
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        unsafe { __pender(self.0) };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -472,15 +436,31 @@ impl SyncExecutor {
 | 
				
			|||||||
///
 | 
					///
 | 
				
			||||||
/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks
 | 
					/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks
 | 
				
			||||||
///   that "want to run").
 | 
					///   that "want to run").
 | 
				
			||||||
/// - You must supply a [`Pender`]. The executor will call it to notify you it has work
 | 
					/// - You must supply a pender function, as shown below. The executor will call it to notify you
 | 
				
			||||||
///   to do. You must arrange for `poll()` to be called as soon as possible.
 | 
					///   it has work to do. You must arrange for `poll()` to be called as soon as possible.
 | 
				
			||||||
 | 
					/// - Enabling `arch-xx` features will define a pender function for you. This means that you
 | 
				
			||||||
 | 
					///   are limited to using the executors provided to you by the architecture/platform
 | 
				
			||||||
 | 
					///   implementation. If you need a different executor, you must not enable `arch-xx` features.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// The [`Pender`] can be called from *any* context: any thread, any interrupt priority
 | 
					/// The pender can be called from *any* context: any thread, any interrupt priority
 | 
				
			||||||
/// level, etc. It may be called synchronously from any `Executor` method call as well.
 | 
					/// level, etc. It may be called synchronously from any `Executor` method call as well.
 | 
				
			||||||
/// You must deal with this correctly.
 | 
					/// You must deal with this correctly.
 | 
				
			||||||
///
 | 
					///
 | 
				
			||||||
/// In particular, you must NOT call `poll` directly from the pender callback, as this violates
 | 
					/// In particular, you must NOT call `poll` directly from the pender callback, as this violates
 | 
				
			||||||
/// the requirement for `poll` to not be called reentrantly.
 | 
					/// the requirement for `poll` to not be called reentrantly.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// The pender function must be exported with the name `__pender` and have the following signature:
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// ```rust
 | 
				
			||||||
 | 
					/// #[export_name = "__pender"]
 | 
				
			||||||
 | 
					/// fn pender(context: *mut ()) {
 | 
				
			||||||
 | 
					///    // schedule `poll()` to be called
 | 
				
			||||||
 | 
					/// }
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// The `context` argument is a piece of arbitrary data the executor will pass to the pender.
 | 
				
			||||||
 | 
					/// You can set the `context` when calling [`Executor::new()`]. You can use it to, for example,
 | 
				
			||||||
 | 
					/// differentiate between executors, or to pass a pointer to a callback that should be called.
 | 
				
			||||||
#[repr(transparent)]
 | 
					#[repr(transparent)]
 | 
				
			||||||
pub struct Executor {
 | 
					pub struct Executor {
 | 
				
			||||||
    pub(crate) inner: SyncExecutor,
 | 
					    pub(crate) inner: SyncExecutor,
 | 
				
			||||||
@@ -495,12 +475,12 @@ impl Executor {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// Create a new executor.
 | 
					    /// Create a new executor.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// When the executor has work to do, it will call the [`Pender`].
 | 
					    /// When the executor has work to do, it will call the pender function and pass `context` to it.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// See [`Executor`] docs for details on `Pender`.
 | 
					    /// See [`Executor`] docs for details on the pender.
 | 
				
			||||||
    pub fn new(pender: Pender) -> Self {
 | 
					    pub fn new(context: *mut ()) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            inner: SyncExecutor::new(pender),
 | 
					            inner: SyncExecutor::new(Pender(context)),
 | 
				
			||||||
            _not_sync: PhantomData,
 | 
					            _not_sync: PhantomData,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -523,16 +503,16 @@ impl Executor {
 | 
				
			|||||||
    /// This loops over all tasks that are queued to be polled (i.e. they're
 | 
					    /// This loops over all tasks that are queued to be polled (i.e. they're
 | 
				
			||||||
    /// freshly spawned or they've been woken). Other tasks are not polled.
 | 
					    /// freshly spawned or they've been woken). Other tasks are not polled.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// You must call `poll` after receiving a call to the [`Pender`]. It is OK
 | 
					    /// You must call `poll` after receiving a call to the pender. It is OK
 | 
				
			||||||
    /// to call `poll` even when not requested by the `Pender`, but it wastes
 | 
					    /// to call `poll` even when not requested by the pender, but it wastes
 | 
				
			||||||
    /// energy.
 | 
					    /// energy.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// # Safety
 | 
					    /// # Safety
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// You must NOT call `poll` reentrantly on the same executor.
 | 
					    /// You must NOT call `poll` reentrantly on the same executor.
 | 
				
			||||||
    ///
 | 
					    ///
 | 
				
			||||||
    /// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you
 | 
					    /// In particular, note that `poll` may call the pender synchronously. Therefore, you
 | 
				
			||||||
    /// must NOT directly call `poll()` from the `Pender` callback. Instead, the callback has to
 | 
					    /// must NOT directly call `poll()` from the pender callback. Instead, the callback has to
 | 
				
			||||||
    /// somehow schedule for `poll()` to be called later, at a time you know for sure there's
 | 
					    /// somehow schedule for `poll()` to be called later, at a time you know for sure there's
 | 
				
			||||||
    /// no `poll()` already running.
 | 
					    /// no `poll()` already running.
 | 
				
			||||||
    pub unsafe fn poll(&'static self) {
 | 
					    pub unsafe fn poll(&'static self) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user