diff options
Diffstat (limited to 'embassy-executor/src')
| -rw-r--r-- | embassy-executor/src/arch/cortex_m.rs | 238 | ||||
| -rw-r--r-- | embassy-executor/src/arch/riscv32.rs | 100 | ||||
| -rw-r--r-- | embassy-executor/src/arch/std.rs | 74 | ||||
| -rw-r--r-- | embassy-executor/src/arch/wasm.rs | 37 | ||||
| -rw-r--r-- | embassy-executor/src/arch/xtensa.rs | 113 | ||||
| -rw-r--r-- | embassy-executor/src/interrupt.rs | 127 | ||||
| -rw-r--r-- | embassy-executor/src/lib.rs | 5 | ||||
| -rw-r--r-- | embassy-executor/src/raw/mod.rs | 35 | ||||
| -rw-r--r-- | embassy-executor/src/thread.rs | 80 |
9 files changed, 433 insertions, 376 deletions
diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 94c8134d6..ca1675c03 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs | |||
| @@ -1,224 +1,98 @@ | |||
| 1 | #[cfg(feature = "executor-thread")] | 1 | #[cfg(feature = "executor-thread")] |
| 2 | pub use thread::*; | 2 | pub use thread::*; |
| 3 | |||
| 3 | #[cfg(feature = "executor-thread")] | 4 | #[cfg(feature = "executor-thread")] |
| 4 | mod thread { | 5 | mod thread { |
| 5 | use core::arch::asm; | ||
| 6 | use core::marker::PhantomData; | ||
| 7 | 6 | ||
| 8 | #[cfg(feature = "nightly")] | 7 | #[cfg(feature = "nightly")] |
| 9 | pub use embassy_macros::main_cortex_m as main; | 8 | pub use embassy_macros::main_cortex_m as main; |
| 10 | 9 | ||
| 11 | use crate::raw::{Pender, PenderInner}; | 10 | use crate::raw::OpaqueThreadContext; |
| 12 | use crate::{raw, Spawner}; | 11 | use crate::thread::ThreadContext; |
| 13 | |||
| 14 | #[derive(Copy, Clone)] | ||
| 15 | pub(crate) struct ThreadPender; | ||
| 16 | 12 | ||
| 17 | impl ThreadPender { | 13 | #[export_name = "__thread_mode_pender"] |
| 18 | pub(crate) fn pend(self) { | 14 | fn __thread_mode_pender(_core_id: OpaqueThreadContext) { |
| 19 | unsafe { core::arch::asm!("sev") } | 15 | unsafe { core::arch::asm!("sev") } |
| 20 | } | ||
| 21 | } | 16 | } |
| 22 | 17 | ||
| 23 | /// Thread mode executor, using WFE/SEV. | 18 | /// TODO |
| 24 | /// | 19 | // Name pending |
| 25 | /// This is the simplest and most common kind of executor. It runs on | 20 | #[derive(Default)] // Default enables Executor::new |
| 26 | /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction | 21 | pub struct CortexMThreadContext { |
| 27 | /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction | 22 | _not_send: core::marker::PhantomData<*mut ()>, |
| 28 | /// is executed, to make the `WFE` exit from sleep and poll the task. | ||
| 29 | /// | ||
| 30 | /// This executor allows for ultra low power consumption for chips where `WFE` | ||
| 31 | /// triggers low-power sleep without extra steps. If your chip requires extra steps, | ||
| 32 | /// you may use [`raw::Executor`] directly to program custom behavior. | ||
| 33 | pub struct Executor { | ||
| 34 | inner: raw::Executor, | ||
| 35 | not_send: PhantomData<*mut ()>, | ||
| 36 | } | 23 | } |
| 37 | 24 | ||
| 38 | impl Executor { | 25 | impl ThreadContext for CortexMThreadContext { |
| 39 | /// Create a new Executor. | 26 | #[cfg(feature = "thread-context")] |
| 40 | pub fn new() -> Self { | 27 | fn context(&self) -> OpaqueThreadContext { |
| 41 | Self { | 28 | // Enabling thread-context is not incorrect, just wasteful. |
| 42 | inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), | 29 | OpaqueThreadContext(0) |
| 43 | not_send: PhantomData, | 30 | } |
| 44 | } | 31 | |
| 32 | #[cfg(not(feature = "thread-context"))] | ||
| 33 | fn context(&self) -> OpaqueThreadContext { | ||
| 34 | OpaqueThreadContext(()) | ||
| 45 | } | 35 | } |
| 46 | 36 | ||
| 47 | /// Run the executor. | 37 | fn wait(&mut self) { |
| 48 | /// | 38 | unsafe { core::arch::asm!("wfe") } |
| 49 | /// The `init` closure is called with a [`Spawner`] that spawns tasks on | ||
| 50 | /// this executor. Use it to spawn the initial task(s). After `init` returns, | ||
| 51 | /// the executor starts running the tasks. | ||
| 52 | /// | ||
| 53 | /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), | ||
| 54 | /// for example by passing it as an argument to the initial tasks. | ||
| 55 | /// | ||
| 56 | /// This function requires `&'static mut self`. This means you have to store the | ||
| 57 | /// Executor instance in a place where it'll live forever and grants you mutable | ||
| 58 | /// access. There's a few ways to do this: | ||
| 59 | /// | ||
| 60 | /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) | ||
| 61 | /// - a `static mut` (unsafe) | ||
| 62 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) | ||
| 63 | /// | ||
| 64 | /// This function never returns. | ||
| 65 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | ||
| 66 | init(self.inner.spawner()); | ||
| 67 | |||
| 68 | loop { | ||
| 69 | unsafe { | ||
| 70 | self.inner.poll(); | ||
| 71 | asm!("wfe"); | ||
| 72 | }; | ||
| 73 | } | ||
| 74 | } | 39 | } |
| 75 | } | 40 | } |
| 41 | |||
| 42 | /// TODO | ||
| 43 | // Type alias for backwards compatibility | ||
| 44 | pub type Executor = crate::thread::ThreadModeExecutor<CortexMThreadContext>; | ||
| 76 | } | 45 | } |
| 77 | 46 | ||
| 47 | // None of this has to be public, I guess? | ||
| 78 | #[cfg(feature = "executor-interrupt")] | 48 | #[cfg(feature = "executor-interrupt")] |
| 79 | pub use interrupt::*; | 49 | pub use interrupt::*; |
| 80 | #[cfg(feature = "executor-interrupt")] | 50 | #[cfg(feature = "executor-interrupt")] |
| 81 | mod interrupt { | 51 | mod interrupt { |
| 82 | use core::cell::UnsafeCell; | ||
| 83 | use core::mem::MaybeUninit; | ||
| 84 | |||
| 85 | use atomic_polyfill::{AtomicBool, Ordering}; | ||
| 86 | use cortex_m::interrupt::InterruptNumber; | 52 | use cortex_m::interrupt::InterruptNumber; |
| 87 | use cortex_m::peripheral::NVIC; | 53 | use cortex_m::peripheral::NVIC; |
| 88 | 54 | ||
| 89 | use crate::raw::{self, Pender, PenderInner}; | 55 | use crate::interrupt::InterruptContext; |
| 56 | use crate::raw::OpaqueInterruptContext; | ||
| 90 | 57 | ||
| 91 | #[derive(Clone, Copy)] | 58 | #[derive(Clone, Copy)] |
| 92 | pub(crate) struct InterruptPender(u16); | 59 | struct CortexMInterruptContext(u16); |
| 93 | |||
| 94 | impl InterruptPender { | ||
| 95 | pub(crate) fn pend(self) { | ||
| 96 | // STIR is faster, but is only available in v7 and higher. | ||
| 97 | #[cfg(not(armv6m))] | ||
| 98 | { | ||
| 99 | let mut nvic: cortex_m::peripheral::NVIC = unsafe { core::mem::transmute(()) }; | ||
| 100 | nvic.request(self); | ||
| 101 | } | ||
| 102 | |||
| 103 | #[cfg(armv6m)] | ||
| 104 | cortex_m::peripheral::NVIC::pend(self); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | 60 | ||
| 108 | unsafe impl cortex_m::interrupt::InterruptNumber for InterruptPender { | 61 | unsafe impl cortex_m::interrupt::InterruptNumber for CortexMInterruptContext { |
| 109 | fn number(self) -> u16 { | 62 | fn number(self) -> u16 { |
| 110 | self.0 | 63 | self.0 |
| 111 | } | 64 | } |
| 112 | } | 65 | } |
| 113 | 66 | ||
| 114 | /// Interrupt mode executor. | 67 | impl<T> InterruptContext for T |
| 115 | /// | 68 | where |
| 116 | /// This executor runs tasks in interrupt mode. The interrupt handler is set up | 69 | T: InterruptNumber, |
| 117 | /// to poll tasks, and when a task is woken the interrupt is pended from software. | 70 | { |
| 118 | /// | 71 | fn context(&self) -> OpaqueInterruptContext { |
| 119 | /// This allows running async tasks at a priority higher than thread mode. One | 72 | OpaqueInterruptContext(self.number() as usize) |
| 120 | /// use case is to leave thread mode free for non-async tasks. Another use case is | ||
| 121 | /// to run multiple executors: one in thread mode for low priority tasks and another in | ||
| 122 | /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower | ||
| 123 | /// priority ones. | ||
| 124 | /// | ||
| 125 | /// It is even possible to run multiple interrupt mode executors at different priorities, | ||
| 126 | /// by assigning different priorities to the interrupts. For an example on how to do this, | ||
| 127 | /// See the 'multiprio' example for 'embassy-nrf'. | ||
| 128 | /// | ||
| 129 | /// To use it, you have to pick an interrupt that won't be used by the hardware. | ||
| 130 | /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). | ||
| 131 | /// If this is not the case, you may use an interrupt from any unused peripheral. | ||
| 132 | /// | ||
| 133 | /// It is somewhat more complex to use, it's recommended to use the thread-mode | ||
| 134 | /// [`Executor`] instead, if it works for your use case. | ||
| 135 | pub struct InterruptExecutor { | ||
| 136 | started: AtomicBool, | ||
| 137 | executor: UnsafeCell<MaybeUninit<raw::Executor>>, | ||
| 138 | } | ||
| 139 | |||
| 140 | unsafe impl Send for InterruptExecutor {} | ||
| 141 | unsafe impl Sync for InterruptExecutor {} | ||
| 142 | |||
| 143 | impl InterruptExecutor { | ||
| 144 | /// Create a new, not started `InterruptExecutor`. | ||
| 145 | #[inline] | ||
| 146 | pub const fn new() -> Self { | ||
| 147 | Self { | ||
| 148 | started: AtomicBool::new(false), | ||
| 149 | executor: UnsafeCell::new(MaybeUninit::uninit()), | ||
| 150 | } | ||
| 151 | } | 73 | } |
| 152 | 74 | ||
| 153 | /// Executor interrupt callback. | 75 | fn enable(&self) { |
| 154 | /// | 76 | unsafe { NVIC::unmask(*self) } |
| 155 | /// # Safety | ||
| 156 | /// | ||
| 157 | /// You MUST call this from the interrupt handler, and from nowhere else. | ||
| 158 | pub unsafe fn on_interrupt(&'static self) { | ||
| 159 | let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; | ||
| 160 | executor.poll(); | ||
| 161 | } | 77 | } |
| 78 | } | ||
| 162 | 79 | ||
| 163 | /// Start the executor. | 80 | #[export_name = "__interrupt_mode_pender"] |
| 164 | /// | 81 | fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext) { |
| 165 | /// This initializes the executor, enables the interrupt, and returns. | 82 | let interrupt = CortexMInterruptContext(unsafe { core::mem::transmute::<_, usize>(interrupt) as u16 }); |
| 166 | /// The executor keeps running in the background through the interrupt. | ||
| 167 | /// | ||
| 168 | /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] | ||
| 169 | /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a | ||
| 170 | /// different "thread" (the interrupt), so spawning tasks on it is effectively | ||
| 171 | /// sending them. | ||
| 172 | /// | ||
| 173 | /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from | ||
| 174 | /// a task running in it. | ||
| 175 | /// | ||
| 176 | /// # Interrupt requirements | ||
| 177 | /// | ||
| 178 | /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). | ||
| 179 | /// | ||
| 180 | /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. | ||
| 181 | /// | ||
| 182 | /// You must set the interrupt priority before calling this method. You MUST NOT | ||
| 183 | /// do it after. | ||
| 184 | /// | ||
| 185 | pub fn start(&'static self, irq: impl InterruptNumber) -> crate::SendSpawner { | ||
| 186 | if self | ||
| 187 | .started | ||
| 188 | .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) | ||
| 189 | .is_err() | ||
| 190 | { | ||
| 191 | panic!("InterruptExecutor::start() called multiple times on the same executor."); | ||
| 192 | } | ||
| 193 | |||
| 194 | unsafe { | ||
| 195 | (&mut *self.executor.get()) | ||
| 196 | .as_mut_ptr() | ||
| 197 | .write(raw::Executor::new(Pender(PenderInner::Interrupt(InterruptPender( | ||
| 198 | irq.number(), | ||
| 199 | ))))) | ||
| 200 | } | ||
| 201 | |||
| 202 | let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; | ||
| 203 | |||
| 204 | unsafe { NVIC::unmask(irq) } | ||
| 205 | |||
| 206 | executor.spawner().make_send() | ||
| 207 | } | ||
| 208 | 83 | ||
| 209 | /// Get a SendSpawner for this executor | 84 | // STIR is faster, but is only available in v7 and higher. |
| 210 | /// | 85 | #[cfg(not(armv6m))] |
| 211 | /// This returns a [`SendSpawner`] you can use to spawn tasks on this | 86 | { |
| 212 | /// executor. | 87 | let mut nvic: NVIC = unsafe { core::mem::transmute(()) }; |
| 213 | /// | 88 | nvic.request(interrupt); |
| 214 | /// This MUST only be called on an executor that has already been spawned. | ||
| 215 | /// The function will panic otherwise. | ||
| 216 | pub fn spawner(&'static self) -> crate::SendSpawner { | ||
| 217 | if !self.started.load(Ordering::Acquire) { | ||
| 218 | panic!("InterruptExecutor::spawner() called on uninitialized executor."); | ||
| 219 | } | ||
| 220 | let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; | ||
| 221 | executor.spawner().make_send() | ||
| 222 | } | 89 | } |
| 90 | |||
| 91 | #[cfg(armv6m)] | ||
| 92 | NVIC::pend(interrupt); | ||
| 223 | } | 93 | } |
| 94 | |||
| 95 | /// TODO | ||
| 96 | // Type alias for backwards compatibility | ||
| 97 | pub type InterruptExecutor = crate::interrupt::InterruptModeExecutor; | ||
| 224 | } | 98 | } |
diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index ff7ec1575..5f766442d 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs | |||
| @@ -1,6 +1,9 @@ | |||
| 1 | #[cfg(feature = "executor-interrupt")] | 1 | #[cfg(feature = "executor-interrupt")] |
| 2 | compile_error!("`executor-interrupt` is not supported with `arch-riscv32`."); | 2 | compile_error!("`executor-interrupt` is not supported with `arch-riscv32`."); |
| 3 | 3 | ||
| 4 | #[cfg(feature = "thread-context")] | ||
| 5 | compile_error!("`thread-context` is not supported with `arch-riscv32`."); | ||
| 6 | |||
| 4 | #[cfg(feature = "executor-thread")] | 7 | #[cfg(feature = "executor-thread")] |
| 5 | pub use thread::*; | 8 | pub use thread::*; |
| 6 | #[cfg(feature = "executor-thread")] | 9 | #[cfg(feature = "executor-thread")] |
| @@ -11,77 +14,50 @@ mod thread { | |||
| 11 | #[cfg(feature = "nightly")] | 14 | #[cfg(feature = "nightly")] |
| 12 | pub use embassy_macros::main_riscv as main; | 15 | pub use embassy_macros::main_riscv as main; |
| 13 | 16 | ||
| 14 | use crate::raw::{Pender, PenderInner}; | 17 | use crate::raw::OpaqueThreadContext; |
| 15 | use crate::{raw, Spawner}; | 18 | use crate::thread::ThreadContext; |
| 16 | |||
| 17 | #[derive(Copy, Clone)] | ||
| 18 | pub(crate) struct ThreadPender; | ||
| 19 | |||
| 20 | impl ThreadPender { | ||
| 21 | #[allow(unused)] | ||
| 22 | pub(crate) fn pend(self) { | ||
| 23 | SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | 19 | ||
| 27 | /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV | 20 | /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV |
| 28 | static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); | 21 | static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); |
| 29 | 22 | ||
| 30 | /// RISCV32 Executor | 23 | #[export_name = "__thread_mode_pender"] |
| 31 | pub struct Executor { | 24 | fn __thread_mode_pender(_core_id: OpaqueThreadContext) { |
| 32 | inner: raw::Executor, | 25 | SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); |
| 33 | not_send: PhantomData<*mut ()>, | ||
| 34 | } | 26 | } |
| 35 | 27 | ||
| 36 | impl Executor { | 28 | /// TODO |
| 37 | /// Create a new Executor. | 29 | // Name pending |
| 38 | pub fn new() -> Self { | 30 | #[derive(Default)] // Default enables Executor::new |
| 39 | Self { | 31 | pub struct RiscVThreadContext { |
| 40 | inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), | 32 | _not_send: PhantomData<*mut ()>, |
| 41 | not_send: PhantomData, | 33 | } |
| 42 | } | ||
| 43 | } | ||
| 44 | 34 | ||
| 45 | /// Run the executor. | 35 | impl ThreadContext for RiscVThreadContext { |
| 46 | /// | 36 | fn context(&self) -> OpaqueThreadContext { |
| 47 | /// The `init` closure is called with a [`Spawner`] that spawns tasks on | 37 | OpaqueThreadContext(()) |
| 48 | /// this executor. Use it to spawn the initial task(s). After `init` returns, | 38 | } |
| 49 | /// the executor starts running the tasks. | ||
| 50 | /// | ||
| 51 | /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), | ||
| 52 | /// for example by passing it as an argument to the initial tasks. | ||
| 53 | /// | ||
| 54 | /// This function requires `&'static mut self`. This means you have to store the | ||
| 55 | /// Executor instance in a place where it'll live forever and grants you mutable | ||
| 56 | /// access. There's a few ways to do this: | ||
| 57 | /// | ||
| 58 | /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) | ||
| 59 | /// - a `static mut` (unsafe) | ||
| 60 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) | ||
| 61 | /// | ||
| 62 | /// This function never returns. | ||
| 63 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | ||
| 64 | init(self.inner.spawner()); | ||
| 65 | 39 | ||
| 66 | loop { | 40 | fn wait(&mut self) { |
| 67 | unsafe { | 41 | // We do not care about race conditions between the load and store operations, |
| 68 | self.inner.poll(); | 42 | // interrupts will only set this value to true. |
| 69 | // we do not care about race conditions between the load and store operations, interrupts | 43 | critical_section::with(|_| { |
| 70 | //will only set this value to true. | 44 | // if there is work to do, loop back to polling |
| 71 | critical_section::with(|_| { | 45 | // TODO can we relax this? |
| 72 | // if there is work to do, loop back to polling | 46 | if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { |
| 73 | // TODO can we relax this? | 47 | SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); |
| 74 | if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { | ||
| 75 | SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); | ||
| 76 | } | ||
| 77 | // if not, wait for interrupt | ||
| 78 | else { | ||
| 79 | core::arch::asm!("wfi"); | ||
| 80 | } | ||
| 81 | }); | ||
| 82 | // if an interrupt occurred while waiting, it will be serviced here | ||
| 83 | } | 48 | } |
| 84 | } | 49 | // if not, wait for interrupt |
| 50 | else { | ||
| 51 | unsafe { | ||
| 52 | core::arch::asm!("wfi"); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | }); | ||
| 56 | // if an interrupt occurred while waiting, it will be serviced here | ||
| 85 | } | 57 | } |
| 86 | } | 58 | } |
| 59 | |||
| 60 | /// TODO | ||
| 61 | // Type alias for backwards compatibility | ||
| 62 | pub type Executor = crate::thread::ThreadModeExecutor<RiscVThreadContext>; | ||
| 87 | } | 63 | } |
diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 4e4a178f0..28e25fbd0 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs | |||
| @@ -1,6 +1,9 @@ | |||
| 1 | #[cfg(feature = "executor-interrupt")] | 1 | #[cfg(feature = "executor-interrupt")] |
| 2 | compile_error!("`executor-interrupt` is not supported with `arch-std`."); | 2 | compile_error!("`executor-interrupt` is not supported with `arch-std`."); |
| 3 | 3 | ||
| 4 | #[cfg(not(feature = "thread-context"))] | ||
| 5 | compile_error!("`arch-std` requires `thread-context`."); | ||
| 6 | |||
| 4 | #[cfg(feature = "executor-thread")] | 7 | #[cfg(feature = "executor-thread")] |
| 5 | pub use thread::*; | 8 | pub use thread::*; |
| 6 | #[cfg(feature = "executor-thread")] | 9 | #[cfg(feature = "executor-thread")] |
| @@ -11,65 +14,42 @@ mod thread { | |||
| 11 | #[cfg(feature = "nightly")] | 14 | #[cfg(feature = "nightly")] |
| 12 | pub use embassy_macros::main_std as main; | 15 | pub use embassy_macros::main_std as main; |
| 13 | 16 | ||
| 14 | use crate::raw::{Pender, PenderInner}; | 17 | use crate::raw::OpaqueThreadContext; |
| 15 | use crate::{raw, Spawner}; | 18 | use crate::thread::ThreadContext; |
| 16 | |||
| 17 | #[derive(Copy, Clone)] | ||
| 18 | pub(crate) struct ThreadPender(&'static Signaler); | ||
| 19 | |||
| 20 | impl ThreadPender { | ||
| 21 | #[allow(unused)] | ||
| 22 | pub(crate) fn pend(self) { | ||
| 23 | self.0.signal() | ||
| 24 | } | ||
| 25 | } | ||
| 26 | 19 | ||
| 27 | /// Single-threaded std-based executor. | 20 | /// TODO |
| 28 | pub struct Executor { | 21 | // Name pending |
| 29 | inner: raw::Executor, | 22 | pub struct StdThreadCtx { |
| 30 | not_send: PhantomData<*mut ()>, | 23 | _not_send: PhantomData<*mut ()>, |
| 31 | signaler: &'static Signaler, | 24 | signaler: &'static Signaler, |
| 32 | } | 25 | } |
| 33 | 26 | ||
| 34 | impl Executor { | 27 | impl Default for StdThreadCtx { |
| 35 | /// Create a new Executor. | 28 | fn default() -> Self { |
| 36 | pub fn new() -> Self { | ||
| 37 | let signaler = &*Box::leak(Box::new(Signaler::new())); | 29 | let signaler = &*Box::leak(Box::new(Signaler::new())); |
| 38 | Self { | 30 | Self { |
| 39 | inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(signaler)))), | 31 | _not_send: PhantomData, |
| 40 | not_send: PhantomData, | ||
| 41 | signaler, | 32 | signaler, |
| 42 | } | 33 | } |
| 43 | } | 34 | } |
| 35 | } | ||
| 44 | 36 | ||
| 45 | /// Run the executor. | 37 | impl ThreadContext for StdThreadCtx { |
| 46 | /// | 38 | fn context(&self) -> OpaqueThreadContext { |
| 47 | /// The `init` closure is called with a [`Spawner`] that spawns tasks on | 39 | OpaqueThreadContext(self.signaler as *const _ as usize) |
| 48 | /// this executor. Use it to spawn the initial task(s). After `init` returns, | 40 | } |
| 49 | /// the executor starts running the tasks. | ||
| 50 | /// | ||
| 51 | /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), | ||
| 52 | /// for example by passing it as an argument to the initial tasks. | ||
| 53 | /// | ||
| 54 | /// This function requires `&'static mut self`. This means you have to store the | ||
| 55 | /// Executor instance in a place where it'll live forever and grants you mutable | ||
| 56 | /// access. There's a few ways to do this: | ||
| 57 | /// | ||
| 58 | /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) | ||
| 59 | /// - a `static mut` (unsafe) | ||
| 60 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) | ||
| 61 | /// | ||
| 62 | /// This function never returns. | ||
| 63 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | ||
| 64 | init(self.inner.spawner()); | ||
| 65 | 41 | ||
| 66 | loop { | 42 | fn wait(&mut self) { |
| 67 | unsafe { self.inner.poll() }; | 43 | self.signaler.wait() |
| 68 | self.signaler.wait() | ||
| 69 | } | ||
| 70 | } | 44 | } |
| 71 | } | 45 | } |
| 72 | 46 | ||
| 47 | #[export_name = "__thread_mode_pender"] | ||
| 48 | fn __thread_mode_pender(core_id: OpaqueThreadContext) { | ||
| 49 | let signaler: &'static Signaler = unsafe { std::mem::transmute(core_id) }; | ||
| 50 | signaler.signal() | ||
| 51 | } | ||
| 52 | |||
| 73 | struct Signaler { | 53 | struct Signaler { |
| 74 | mutex: Mutex<bool>, | 54 | mutex: Mutex<bool>, |
| 75 | condvar: Condvar, | 55 | condvar: Condvar, |
| @@ -97,4 +77,8 @@ mod thread { | |||
| 97 | self.condvar.notify_one(); | 77 | self.condvar.notify_one(); |
| 98 | } | 78 | } |
| 99 | } | 79 | } |
| 80 | |||
| 81 | /// TODO | ||
| 82 | // Type alias for backwards compatibility | ||
| 83 | pub type Executor = crate::thread::ThreadModeExecutor<StdThreadCtx>; | ||
| 100 | } | 84 | } |
diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 08ab16b99..4f5ce9c90 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs | |||
| @@ -1,6 +1,9 @@ | |||
| 1 | #[cfg(feature = "executor-interrupt")] | 1 | #[cfg(feature = "executor-interrupt")] |
| 2 | compile_error!("`executor-interrupt` is not supported with `arch-wasm`."); | 2 | compile_error!("`executor-interrupt` is not supported with `arch-wasm`."); |
| 3 | 3 | ||
| 4 | #[cfg(not(feature = "thread-context"))] | ||
| 5 | compile_error!("`arch-wasm` requires `thread-context`."); | ||
| 6 | |||
| 4 | #[cfg(feature = "executor-thread")] | 7 | #[cfg(feature = "executor-thread")] |
| 5 | pub use thread::*; | 8 | pub use thread::*; |
| 6 | #[cfg(feature = "executor-thread")] | 9 | #[cfg(feature = "executor-thread")] |
| @@ -14,14 +17,13 @@ mod thread { | |||
| 14 | use wasm_bindgen::prelude::*; | 17 | use wasm_bindgen::prelude::*; |
| 15 | 18 | ||
| 16 | use crate::raw::util::UninitCell; | 19 | use crate::raw::util::UninitCell; |
| 17 | use crate::raw::{Pender, PenderInner}; | 20 | use crate::raw::{OpaqueThreadContext, Pender, PenderInner}; |
| 18 | use crate::{raw, Spawner}; | 21 | use crate::{raw, Spawner}; |
| 19 | 22 | ||
| 20 | /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. | 23 | #[export_name = "__thread_mode_pender"] |
| 21 | pub struct Executor { | 24 | fn __thread_mode_pender(context: OpaqueThreadContext) { |
| 22 | inner: raw::Executor, | 25 | let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; |
| 23 | ctx: &'static WasmContext, | 26 | let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); |
| 24 | not_send: PhantomData<*mut ()>, | ||
| 25 | } | 27 | } |
| 26 | 28 | ||
| 27 | pub(crate) struct WasmContext { | 29 | pub(crate) struct WasmContext { |
| @@ -29,16 +31,6 @@ mod thread { | |||
| 29 | closure: UninitCell<Closure<dyn FnMut(JsValue)>>, | 31 | closure: UninitCell<Closure<dyn FnMut(JsValue)>>, |
| 30 | } | 32 | } |
| 31 | 33 | ||
| 32 | #[derive(Copy, Clone)] | ||
| 33 | pub(crate) struct ThreadPender(&'static WasmContext); | ||
| 34 | |||
| 35 | impl ThreadPender { | ||
| 36 | #[allow(unused)] | ||
| 37 | pub(crate) fn pend(self) { | ||
| 38 | let _ = self.0.promise.then(unsafe { self.0.closure.as_mut() }); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | impl WasmContext { | 34 | impl WasmContext { |
| 43 | pub fn new() -> Self { | 35 | pub fn new() -> Self { |
| 44 | Self { | 36 | Self { |
| @@ -48,14 +40,23 @@ mod thread { | |||
| 48 | } | 40 | } |
| 49 | } | 41 | } |
| 50 | 42 | ||
| 43 | /// WASM executor, wasm_bindgen to schedule tasks on the JS event loop. | ||
| 44 | pub struct Executor { | ||
| 45 | inner: raw::Executor, | ||
| 46 | ctx: &'static WasmContext, | ||
| 47 | not_send: PhantomData<*mut ()>, | ||
| 48 | } | ||
| 49 | |||
| 51 | impl Executor { | 50 | impl Executor { |
| 52 | /// Create a new Executor. | 51 | /// Create a new Executor. |
| 53 | pub fn new() -> Self { | 52 | pub fn new() -> Self { |
| 54 | let ctx = &*Box::leak(Box::new(WasmContext::new())); | 53 | let ctx = &*Box::leak(Box::new(WasmContext::new())); |
| 55 | Self { | 54 | Self { |
| 56 | inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender(ctx)))), | 55 | inner: raw::Executor::new(Pender(PenderInner::Thread(OpaqueThreadContext( |
| 57 | not_send: PhantomData, | 56 | ctx as *const _ as usize, |
| 57 | )))), | ||
| 58 | ctx, | 58 | ctx, |
| 59 | not_send: PhantomData, | ||
| 59 | } | 60 | } |
| 60 | } | 61 | } |
| 61 | 62 | ||
diff --git a/embassy-executor/src/arch/xtensa.rs b/embassy-executor/src/arch/xtensa.rs index 017b2c52b..8e1b917de 100644 --- a/embassy-executor/src/arch/xtensa.rs +++ b/embassy-executor/src/arch/xtensa.rs | |||
| @@ -1,6 +1,12 @@ | |||
| 1 | #[cfg(feature = "executor-interrupt")] | 1 | #[cfg(feature = "executor-interrupt")] |
| 2 | compile_error!("`executor-interrupt` is not supported with `arch-xtensa`."); | 2 | compile_error!("`executor-interrupt` is not supported with `arch-xtensa`."); |
| 3 | 3 | ||
| 4 | #[cfg(feature = "thread-context")] | ||
| 5 | compile_error!( | ||
| 6 | "`thread-context` is not supported with `arch-xtensa`.\ | ||
| 7 | Use a multicore-safe executor from esp-hal instead." // obviously, this is too specific to ESP32 | ||
| 8 | ); | ||
| 9 | |||
| 4 | #[cfg(feature = "executor-thread")] | 10 | #[cfg(feature = "executor-thread")] |
| 5 | pub use thread::*; | 11 | pub use thread::*; |
| 6 | #[cfg(feature = "executor-thread")] | 12 | #[cfg(feature = "executor-thread")] |
| @@ -8,86 +14,63 @@ mod thread { | |||
| 8 | use core::marker::PhantomData; | 14 | use core::marker::PhantomData; |
| 9 | use core::sync::atomic::{AtomicBool, Ordering}; | 15 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 10 | 16 | ||
| 11 | use crate::raw::{Pender, PenderInner}; | 17 | use crate::raw::OpaqueThreadContext; |
| 12 | use crate::{raw, Spawner}; | 18 | use crate::thread::ThreadContext; |
| 13 | 19 | ||
| 14 | #[derive(Copy, Clone)] | 20 | /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV |
| 15 | pub(crate) struct ThreadPender; | 21 | static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); |
| 16 | 22 | ||
| 17 | impl ThreadPender { | 23 | #[export_name = "__thread_mode_pender"] |
| 18 | #[allow(unused)] | 24 | fn __thread_mode_pender(_core_id: OpaqueThreadContext) { |
| 19 | pub(crate) fn pend(self) { | 25 | SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); |
| 20 | SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); | ||
| 21 | } | ||
| 22 | } | 26 | } |
| 23 | 27 | ||
| 24 | /// global atomic used to keep track of whether there is work to do since sev() is not available on Xtensa | 28 | /// TODO |
| 25 | static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); | 29 | // Name pending |
| 26 | 30 | pub struct XtensaThreadContext { | |
| 27 | /// Xtensa Executor | 31 | _not_send: PhantomData<*mut ()>, |
| 28 | pub struct Executor { | ||
| 29 | inner: raw::Executor, | ||
| 30 | not_send: PhantomData<*mut ()>, | ||
| 31 | } | 32 | } |
| 32 | 33 | ||
| 33 | impl Executor { | 34 | impl Default for XtensaThreadContext { |
| 34 | /// Create a new Executor. | 35 | fn default() -> Self { |
| 35 | pub fn new() -> Self { | 36 | Self { _not_send: PhantomData } |
| 36 | Self { | ||
| 37 | inner: raw::Executor::new(Pender(PenderInner::Thread(ThreadPender))), | ||
| 38 | not_send: PhantomData, | ||
| 39 | } | ||
| 40 | } | 37 | } |
| 38 | } | ||
| 41 | 39 | ||
| 42 | /// Run the executor. | 40 | impl ThreadContext for XtensaThreadContext { |
| 43 | /// | 41 | fn context(&self) -> OpaqueThreadContext { |
| 44 | /// The `init` closure is called with a [`Spawner`] that spawns tasks on | 42 | OpaqueThreadContext(()) |
| 45 | /// this executor. Use it to spawn the initial task(s). After `init` returns, | 43 | } |
| 46 | /// the executor starts running the tasks. | ||
| 47 | /// | ||
| 48 | /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), | ||
| 49 | /// for example by passing it as an argument to the initial tasks. | ||
| 50 | /// | ||
| 51 | /// This function requires `&'static mut self`. This means you have to store the | ||
| 52 | /// Executor instance in a place where it'll live forever and grants you mutable | ||
| 53 | /// access. There's a few ways to do this: | ||
| 54 | /// | ||
| 55 | /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) | ||
| 56 | /// - a `static mut` (unsafe) | ||
| 57 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) | ||
| 58 | /// | ||
| 59 | /// This function never returns. | ||
| 60 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | ||
| 61 | init(self.inner.spawner()); | ||
| 62 | |||
| 63 | loop { | ||
| 64 | unsafe { | ||
| 65 | self.inner.poll(); | ||
| 66 | 44 | ||
| 67 | // Manual critical section implementation that only masks interrupts handlers. | 45 | fn wait(&mut self) { |
| 68 | // We must not acquire the cross-core on dual-core systems because that would | 46 | unsafe { |
| 69 | // prevent the other core from doing useful work while this core is sleeping. | 47 | // Manual critical section implementation that only masks interrupts handlers. |
| 70 | let token: critical_section::RawRestoreState; | 48 | // We must not acquire the cross-core on dual-core systems because that would |
| 71 | core::arch::asm!("rsil {0}, 5", out(reg) token); | 49 | // prevent the other core from doing useful work while this core is sleeping. |
| 50 | let token: critical_section::RawRestoreState; | ||
| 51 | core::arch::asm!("rsil {0}, 5", out(reg) token); | ||
| 72 | 52 | ||
| 73 | // we do not care about race conditions between the load and store operations, interrupts | 53 | // we do not care about race conditions between the load and store operations, interrupts |
| 74 | // will only set this value to true. | 54 | // will only set this value to true. |
| 75 | // if there is work to do, loop back to polling | 55 | // if there is work to do, loop back to polling |
| 76 | if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { | 56 | if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { |
| 77 | SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); | 57 | SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); |
| 78 | 58 | ||
| 79 | core::arch::asm!( | 59 | core::arch::asm!( |
| 80 | "wsr.ps {0}", | 60 | "wsr.ps {0}", |
| 81 | "rsync", in(reg) token) | 61 | "rsync", in(reg) token) |
| 82 | } else { | 62 | } else { |
| 83 | // waiti sets the PS.INTLEVEL when slipping into sleep | 63 | // waiti sets the PS.INTLEVEL when slipping into sleep |
| 84 | // because critical sections in Xtensa are implemented via increasing | 64 | // because critical sections in Xtensa are implemented via increasing |
| 85 | // PS.INTLEVEL the critical section ends here | 65 | // PS.INTLEVEL the critical section ends here |
| 86 | // take care not add code after `waiti` if it needs to be inside the CS | 66 | // take care not add code after `waiti` if it needs to be inside the CS |
| 87 | core::arch::asm!("waiti 0"); // critical section ends here | 67 | core::arch::asm!("waiti 0"); // critical section ends here |
| 88 | } | ||
| 89 | } | 68 | } |
| 90 | } | 69 | } |
| 91 | } | 70 | } |
| 92 | } | 71 | } |
| 72 | |||
| 73 | /// TODO | ||
| 74 | // Type alias for backwards compatibility | ||
| 75 | pub type Executor = crate::thread::ThreadModeExecutor<XtensaThreadContext>; | ||
| 93 | } | 76 | } |
diff --git a/embassy-executor/src/interrupt.rs b/embassy-executor/src/interrupt.rs new file mode 100644 index 000000000..f8b0809da --- /dev/null +++ b/embassy-executor/src/interrupt.rs | |||
| @@ -0,0 +1,127 @@ | |||
| 1 | //! Interrupt-mode executor. | ||
| 2 | |||
| 3 | use core::cell::UnsafeCell; | ||
| 4 | use core::mem::MaybeUninit; | ||
| 5 | |||
| 6 | use atomic_polyfill::{AtomicBool, Ordering}; | ||
| 7 | |||
| 8 | use crate::raw::{self, OpaqueInterruptContext, Pender, PenderInner}; | ||
| 9 | |||
| 10 | /// An interrupt source that can be used to drive an [`InterruptExecutor`]. | ||
| 11 | // Name pending | ||
| 12 | pub trait InterruptContext { | ||
| 13 | /// Creates an opaque identifier for this interrupt. | ||
| 14 | fn context(&self) -> OpaqueInterruptContext; | ||
| 15 | |||
| 16 | /// Sets up the interrupt request. | ||
| 17 | fn enable(&self); | ||
| 18 | } | ||
| 19 | |||
| 20 | /// Interrupt mode executor. | ||
| 21 | /// | ||
| 22 | /// This executor runs tasks in interrupt mode. The interrupt handler is set up | ||
| 23 | /// to poll tasks, and when a task is woken the interrupt is pended from software. | ||
| 24 | /// | ||
| 25 | /// This allows running async tasks at a priority higher than thread mode. One | ||
| 26 | /// use case is to leave thread mode free for non-async tasks. Another use case is | ||
| 27 | /// to run multiple executors: one in thread mode for low priority tasks and another in | ||
| 28 | /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower | ||
| 29 | /// priority ones. | ||
| 30 | /// | ||
| 31 | /// It is even possible to run multiple interrupt mode executors at different priorities, | ||
| 32 | /// by assigning different priorities to the interrupts. For an example on how to do this, | ||
| 33 | /// See the 'multiprio' example for 'embassy-nrf'. | ||
| 34 | /// | ||
| 35 | /// To use it, you have to pick an interrupt that won't be used by the hardware. | ||
| 36 | /// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI). | ||
| 37 | /// If this is not the case, you may use an interrupt from any unused peripheral. | ||
| 38 | /// | ||
| 39 | /// It is somewhat more complex to use, it's recommended to use the thread-mode | ||
| 40 | /// [`Executor`] instead, if it works for your use case. | ||
| 41 | pub struct InterruptModeExecutor { | ||
| 42 | started: AtomicBool, | ||
| 43 | executor: UnsafeCell<MaybeUninit<raw::Executor>>, | ||
| 44 | } | ||
| 45 | |||
| 46 | unsafe impl Send for InterruptModeExecutor {} | ||
| 47 | unsafe impl Sync for InterruptModeExecutor {} | ||
| 48 | |||
| 49 | impl InterruptModeExecutor { | ||
| 50 | /// Create a new, not started `InterruptExecutor`. | ||
| 51 | #[inline] | ||
| 52 | pub const fn new() -> Self { | ||
| 53 | Self { | ||
| 54 | started: AtomicBool::new(false), | ||
| 55 | executor: UnsafeCell::new(MaybeUninit::uninit()), | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | /// Executor interrupt callback. | ||
| 60 | /// | ||
| 61 | /// # Safety | ||
| 62 | /// | ||
| 63 | /// You MUST call this from the interrupt handler, and from nowhere else. | ||
| 64 | pub unsafe fn on_interrupt(&'static self) { | ||
| 65 | let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; | ||
| 66 | executor.poll(); | ||
| 67 | } | ||
| 68 | |||
| 69 | /// Start the executor. | ||
| 70 | /// | ||
| 71 | /// This initializes the executor, enables the interrupt, and returns. | ||
| 72 | /// The executor keeps running in the background through the interrupt. | ||
| 73 | /// | ||
| 74 | /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] | ||
| 75 | /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a | ||
| 76 | /// different "thread" (the interrupt), so spawning tasks on it is effectively | ||
| 77 | /// sending them. | ||
| 78 | /// | ||
| 79 | /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from | ||
| 80 | /// a task running in it. | ||
| 81 | /// | ||
| 82 | /// # Interrupt requirements | ||
| 83 | /// | ||
| 84 | /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). | ||
| 85 | /// | ||
| 86 | /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. | ||
| 87 | /// | ||
| 88 | /// You must set the interrupt priority before calling this method. You MUST NOT | ||
| 89 | /// do it after. | ||
| 90 | /// | ||
| 91 | pub fn start(&'static self, irq: impl InterruptContext) -> crate::SendSpawner { | ||
| 92 | if self | ||
| 93 | .started | ||
| 94 | .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) | ||
| 95 | .is_err() | ||
| 96 | { | ||
| 97 | panic!("InterruptExecutor::start() called multiple times on the same executor."); | ||
| 98 | } | ||
| 99 | |||
| 100 | unsafe { | ||
| 101 | (&mut *self.executor.get()) | ||
| 102 | .as_mut_ptr() | ||
| 103 | .write(raw::Executor::new(Pender(PenderInner::Interrupt(irq.context())))) | ||
| 104 | } | ||
| 105 | |||
| 106 | let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; | ||
| 107 | |||
| 108 | irq.enable(); | ||
| 109 | |||
| 110 | executor.spawner().make_send() | ||
| 111 | } | ||
| 112 | |||
| 113 | /// Get a SendSpawner for this executor | ||
| 114 | /// | ||
| 115 | /// This returns a [`SendSpawner`] you can use to spawn tasks on this | ||
| 116 | /// executor. | ||
| 117 | /// | ||
| 118 | /// This MUST only be called on an executor that has already been spawned. | ||
| 119 | /// The function will panic otherwise. | ||
| 120 | pub fn spawner(&'static self) -> crate::SendSpawner { | ||
| 121 | if !self.started.load(Ordering::Acquire) { | ||
| 122 | panic!("InterruptExecutor::spawner() called on uninitialized executor."); | ||
| 123 | } | ||
| 124 | let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; | ||
| 125 | executor.spawner().make_send() | ||
| 126 | } | ||
| 127 | } | ||
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 3ce687eb6..ca67c9484 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs | |||
| @@ -37,6 +37,11 @@ pub use arch::*; | |||
| 37 | 37 | ||
| 38 | pub mod raw; | 38 | pub mod raw; |
| 39 | 39 | ||
| 40 | #[cfg(feature = "executor-interrupt")] | ||
| 41 | pub mod interrupt; | ||
| 42 | #[cfg(feature = "executor-thread")] | ||
| 43 | pub mod thread; | ||
| 44 | |||
| 40 | mod spawner; | 45 | mod spawner; |
| 41 | pub use spawner::*; | 46 | pub use spawner::*; |
| 42 | 47 | ||
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 25c2ab0da..b4d70b1e9 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs | |||
| @@ -291,12 +291,29 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> { | |||
| 291 | } | 291 | } |
| 292 | } | 292 | } |
| 293 | 293 | ||
| 294 | /// Context given to the thread-mode executor's pender. | ||
| 295 | #[cfg(all(feature = "executor-thread", not(feature = "thread-context")))] | ||
| 296 | #[derive(Clone, Copy)] | ||
| 297 | #[repr(transparent)] | ||
| 298 | pub struct OpaqueThreadContext(pub(crate) ()); | ||
| 299 | |||
| 300 | /// Context given to the thread-mode executor's pender. | ||
| 301 | #[cfg(all(feature = "executor-thread", feature = "thread-context"))] | ||
| 302 | #[repr(transparent)] | ||
| 303 | #[derive(Clone, Copy)] | ||
| 304 | pub struct OpaqueThreadContext(pub(crate) usize); | ||
| 305 | |||
| 306 | /// Context given to the interrupt-mode executor's pender. | ||
| 307 | #[derive(Clone, Copy)] | ||
| 308 | #[repr(transparent)] | ||
| 309 | pub struct OpaqueInterruptContext(pub(crate) usize); | ||
| 310 | |||
| 294 | #[derive(Clone, Copy)] | 311 | #[derive(Clone, Copy)] |
| 295 | pub(crate) enum PenderInner { | 312 | pub(crate) enum PenderInner { |
| 296 | #[cfg(feature = "executor-thread")] | 313 | #[cfg(feature = "executor-thread")] |
| 297 | Thread(crate::arch::ThreadPender), | 314 | Thread(OpaqueThreadContext), |
| 298 | #[cfg(feature = "executor-interrupt")] | 315 | #[cfg(feature = "executor-interrupt")] |
| 299 | Interrupt(crate::arch::InterruptPender), | 316 | Interrupt(OpaqueInterruptContext), |
| 300 | #[cfg(feature = "pender-callback")] | 317 | #[cfg(feature = "pender-callback")] |
| 301 | Callback { func: fn(*mut ()), context: *mut () }, | 318 | Callback { func: fn(*mut ()), context: *mut () }, |
| 302 | } | 319 | } |
| @@ -333,9 +350,19 @@ impl Pender { | |||
| 333 | pub(crate) fn pend(&self) { | 350 | pub(crate) fn pend(&self) { |
| 334 | match self.0 { | 351 | match self.0 { |
| 335 | #[cfg(feature = "executor-thread")] | 352 | #[cfg(feature = "executor-thread")] |
| 336 | PenderInner::Thread(x) => x.pend(), | 353 | PenderInner::Thread(core_id) => { |
| 354 | extern "Rust" { | ||
| 355 | fn __thread_mode_pender(core_id: OpaqueThreadContext); | ||
| 356 | } | ||
| 357 | unsafe { __thread_mode_pender(core_id) }; | ||
| 358 | } | ||
| 337 | #[cfg(feature = "executor-interrupt")] | 359 | #[cfg(feature = "executor-interrupt")] |
| 338 | PenderInner::Interrupt(x) => x.pend(), | 360 | PenderInner::Interrupt(interrupt) => { |
| 361 | extern "Rust" { | ||
| 362 | fn __interrupt_mode_pender(interrupt: OpaqueInterruptContext); | ||
| 363 | } | ||
| 364 | unsafe { __interrupt_mode_pender(interrupt) }; | ||
| 365 | } | ||
| 339 | #[cfg(feature = "pender-callback")] | 366 | #[cfg(feature = "pender-callback")] |
| 340 | PenderInner::Callback { func, context } => func(context), | 367 | PenderInner::Callback { func, context } => func(context), |
| 341 | } | 368 | } |
diff --git a/embassy-executor/src/thread.rs b/embassy-executor/src/thread.rs new file mode 100644 index 000000000..9bbe29500 --- /dev/null +++ b/embassy-executor/src/thread.rs | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | //! Thread-mode executor. | ||
| 2 | |||
| 3 | use core::marker::PhantomData; | ||
| 4 | |||
| 5 | use crate::raw::{OpaqueThreadContext, Pender, PenderInner}; | ||
| 6 | use crate::{raw, Spawner}; | ||
| 7 | |||
| 8 | /// TODO | ||
| 9 | // Name pending | ||
| 10 | pub trait ThreadContext: Sized { | ||
| 11 | /// TODO | ||
| 12 | fn context(&self) -> OpaqueThreadContext; | ||
| 13 | |||
| 14 | /// TODO | ||
| 15 | fn wait(&mut self); | ||
| 16 | } | ||
| 17 | |||
| 18 | /// Thread mode executor, using WFE/SEV. | ||
| 19 | /// | ||
| 20 | /// This is the simplest and most common kind of executor. It runs on | ||
| 21 | /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction | ||
| 22 | /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction | ||
| 23 | /// is executed, to make the `WFE` exit from sleep and poll the task. | ||
| 24 | /// | ||
| 25 | /// This executor allows for ultra low power consumption for chips where `WFE` | ||
| 26 | /// triggers low-power sleep without extra steps. If your chip requires extra steps, | ||
| 27 | /// you may use [`raw::Executor`] directly to program custom behavior. | ||
| 28 | pub struct ThreadModeExecutor<C: ThreadContext> { | ||
| 29 | inner: raw::Executor, | ||
| 30 | context: C, | ||
| 31 | not_send: PhantomData<*mut ()>, | ||
| 32 | } | ||
| 33 | |||
| 34 | impl<C: ThreadContext> ThreadModeExecutor<C> { | ||
| 35 | /// Create a new Executor. | ||
| 36 | pub fn new() -> Self | ||
| 37 | where | ||
| 38 | C: Default, | ||
| 39 | { | ||
| 40 | Self::with_context(C::default()) | ||
| 41 | } | ||
| 42 | |||
| 43 | /// Create a new Executor. | ||
| 44 | pub fn with_context(context: C) -> Self { | ||
| 45 | Self { | ||
| 46 | inner: raw::Executor::new(Pender(PenderInner::Thread(context.context()))), | ||
| 47 | context, | ||
| 48 | not_send: PhantomData, | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Run the executor. | ||
| 53 | /// | ||
| 54 | /// The `init` closure is called with a [`Spawner`] that spawns tasks on | ||
| 55 | /// this executor. Use it to spawn the initial task(s). After `init` returns, | ||
| 56 | /// the executor starts running the tasks. | ||
| 57 | /// | ||
| 58 | /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), | ||
| 59 | /// for example by passing it as an argument to the initial tasks. | ||
| 60 | /// | ||
| 61 | /// This function requires `&'static mut self`. This means you have to store the | ||
| 62 | /// Executor instance in a place where it'll live forever and grants you mutable | ||
| 63 | /// access. There's a few ways to do this: | ||
| 64 | /// | ||
| 65 | /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) | ||
| 66 | /// - a `static mut` (unsafe) | ||
| 67 | /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) | ||
| 68 | /// | ||
| 69 | /// This function never returns. | ||
| 70 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | ||
| 71 | init(self.inner.spawner()); | ||
| 72 | |||
| 73 | loop { | ||
| 74 | unsafe { | ||
| 75 | self.inner.poll(); | ||
| 76 | self.context.wait(); | ||
| 77 | }; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | } | ||
