diff --git a/embassy/src/executor/arch/arm.rs b/embassy/src/executor/arch/arm.rs new file mode 100644 index 00000000..4fd734cd --- /dev/null +++ b/embassy/src/executor/arch/arm.rs @@ -0,0 +1,76 @@ +use core::marker::PhantomData; +use core::ptr; + +use super::{raw, Spawner}; +use crate::interrupt::{Interrupt, InterruptExt}; + +pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, +} + +impl Executor { + pub fn new() -> Self { + Self { + inner: raw::Executor::new(|_| cortex_m::asm::sev(), ptr::null_mut()), + not_send: PhantomData, + } + } + + /// Runs the executor. + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(unsafe { self.inner.spawner() }); + + loop { + unsafe { self.inner.run_queued() }; + cortex_m::asm::wfe(); + } + } +} + +fn pend_by_number(n: u16) { + #[derive(Clone, Copy)] + struct N(u16); + unsafe impl cortex_m::interrupt::InterruptNumber for N { + fn number(self) -> u16 { + self.0 + } + } + cortex_m::peripheral::NVIC::pend(N(n)) +} + +pub struct InterruptExecutor { + irq: I, + inner: raw::Executor, + not_send: PhantomData<*mut ()>, +} + +impl InterruptExecutor { + pub fn new(irq: I) -> Self { + let ctx = irq.number() as *mut (); + Self { + irq, + inner: raw::Executor::new(|ctx| pend_by_number(ctx as u16), ctx), + not_send: PhantomData, + } + } + + /// Start the executor. + /// + /// `init` is called in the interrupt context, then the interrupt is + /// configured to run the executor. + pub fn start(&'static mut self, init: impl FnOnce(Spawner) + Send) { + self.irq.disable(); + + init(unsafe { self.inner.spawner() }); + + self.irq.set_handler(|ctx| unsafe { + let executor = &*(ctx as *const raw::Executor); + executor.run_queued(); + }); + self.irq.set_handler_context(&self.inner as *const _ as _); + self.irq.enable(); + } +} diff --git a/embassy/src/executor/mod.rs b/embassy/src/executor/mod.rs index f3c87729..e0ac566f 100644 --- a/embassy/src/executor/mod.rs +++ b/embassy/src/executor/mod.rs @@ -1,183 +1,7 @@ -use core::marker::PhantomData; -use core::ptr::NonNull; -use core::{mem, ptr}; - +#[path = "arch/arm.rs"] +mod arch; pub mod raw; -mod run_queue; -#[cfg(feature = "time")] -mod timer_queue; -mod util; -mod waker; +mod spawner; -use crate::interrupt::{Interrupt, InterruptExt}; - -#[must_use = "Calling a task function does nothing on its own. You must pass the returned SpawnToken to Executor::spawn()"] -pub struct SpawnToken { - raw_task: Option>, - phantom: PhantomData<*mut F>, -} - -impl Drop for SpawnToken { - fn drop(&mut self) { - // TODO deallocate the task instead. - panic!("SpawnToken instances may not be dropped. You must pass them to Executor::spawn()") - } -} - -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum SpawnError { - Busy, -} - -/// Handle to spawn tasks into an executor. -/// -/// This Spawner can spawn any task (Send and non-Send ones), but it can -/// only be used in the executor thread (it is not Send itself). -/// -/// If you want to spawn tasks from another thread, use [SendSpawner]. -#[derive(Copy, Clone)] -pub struct Spawner { - executor: &'static raw::Executor, - not_send: PhantomData<*mut ()>, -} - -impl Spawner { - pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { - let task = token.raw_task; - mem::forget(token); - - match task { - Some(task) => { - unsafe { self.executor.spawn(task) }; - Ok(()) - } - None => Err(SpawnError::Busy), - } - } - - /// Used by the `embassy_macros::main!` macro to throw an error when spawn - /// fails. This is here to allow conditional use of `defmt::unwrap!` - /// without introducing a `defmt` feature in the `embassy_macros` package, - /// which would require use of `-Z namespaced-features`. - pub fn must_spawn(&self, token: SpawnToken) -> () { - unwrap!(self.spawn(token)); - } - - /// Convert this Spawner to a SendSpawner. This allows you to send the - /// spawner to other threads, but the spawner loses the ability to spawn - /// non-Send tasks. - pub fn make_send(&self) -> SendSpawner { - SendSpawner { - executor: self.executor, - not_send: PhantomData, - } - } -} - -/// Handle to spawn tasks into an executor from any thread. -/// -/// This Spawner can be used from any thread (it implements Send and Sync, so after any task (Send and non-Send ones), but it can -/// only be used in the executor thread (it is not Send itself). -/// -/// If you want to spawn tasks from another thread, use [SendSpawner]. -#[derive(Copy, Clone)] -pub struct SendSpawner { - executor: &'static raw::Executor, - not_send: PhantomData<*mut ()>, -} - -unsafe impl Send for SendSpawner {} -unsafe impl Sync for SendSpawner {} - -/// Handle to spawn tasks to an executor. -/// -/// This Spawner can spawn any task (Send and non-Send ones), but it can -/// only be used in the executor thread (it is not Send itself). -/// -/// If you want to spawn tasks from another thread, use [SendSpawner]. -impl SendSpawner { - pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { - let header = token.raw_task; - mem::forget(token); - - match header { - Some(header) => { - unsafe { self.executor.spawn(header) }; - Ok(()) - } - None => Err(SpawnError::Busy), - } - } -} - -pub struct Executor { - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} - -impl Executor { - pub fn new() -> Self { - Self { - inner: raw::Executor::new(|_| cortex_m::asm::sev(), ptr::null_mut()), - not_send: PhantomData, - } - } - - /// Runs the executor. - /// - /// This function never returns. - pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(unsafe { self.inner.spawner() }); - - loop { - unsafe { self.inner.run_queued() }; - cortex_m::asm::wfe(); - } - } -} - -fn pend_by_number(n: u16) { - #[derive(Clone, Copy)] - struct N(u16); - unsafe impl cortex_m::interrupt::InterruptNumber for N { - fn number(self) -> u16 { - self.0 - } - } - cortex_m::peripheral::NVIC::pend(N(n)) -} - -pub struct InterruptExecutor { - irq: I, - inner: raw::Executor, - not_send: PhantomData<*mut ()>, -} - -impl InterruptExecutor { - pub fn new(irq: I) -> Self { - let ctx = irq.number() as *mut (); - Self { - irq, - inner: raw::Executor::new(|ctx| pend_by_number(ctx as u16), ctx), - not_send: PhantomData, - } - } - - /// Start the executor. - /// - /// `init` is called in the interrupt context, then the interrupt is - /// configured to run the executor. - pub fn start(&'static mut self, init: impl FnOnce(Spawner) + Send) { - self.irq.disable(); - - init(unsafe { self.inner.spawner() }); - - self.irq.set_handler(|ctx| unsafe { - let executor = &*(ctx as *const raw::Executor); - executor.run_queued(); - }); - self.irq.set_handler_context(&self.inner as *const _ as _); - self.irq.enable(); - } -} +pub use arch::*; +pub use spawner::*; diff --git a/embassy/src/executor/raw.rs b/embassy/src/executor/raw/mod.rs similarity index 89% rename from embassy/src/executor/raw.rs rename to embassy/src/executor/raw/mod.rs index fac46d1f..235a0919 100644 --- a/embassy/src/executor/raw.rs +++ b/embassy/src/executor/raw/mod.rs @@ -1,24 +1,27 @@ +mod run_queue; +#[cfg(feature = "time")] +mod timer_queue; +mod util; +mod waker; + use atomic_polyfill::{AtomicU32, Ordering}; use core::cell::Cell; use core::future::Future; -use core::marker::PhantomData; use core::pin::Pin; use core::ptr::NonNull; use core::task::{Context, Poll}; use core::{mem, ptr}; -use super::run_queue::{RunQueue, RunQueueItem}; -use super::util::UninitCell; -use super::waker; +use self::run_queue::{RunQueue, RunQueueItem}; +use self::util::UninitCell; use super::SpawnToken; - -#[cfg(feature = "time")] -use super::timer_queue::{TimerQueue, TimerQueueItem}; #[cfg(feature = "time")] use crate::time::driver::{self, AlarmHandle}; #[cfg(feature = "time")] use crate::time::Instant; +pub use self::waker::task_from_waker; + /// Task is spawned (has a future) pub(crate) const STATE_SPAWNED: u32 = 1 << 0; /// Task is in the executor run queue @@ -36,7 +39,7 @@ pub struct TaskHeader { #[cfg(feature = "time")] pub(crate) expires_at: Cell, #[cfg(feature = "time")] - pub(crate) timer_queue_item: TimerQueueItem, + pub(crate) timer_queue_item: timer_queue::TimerQueueItem, } impl TaskHeader { @@ -50,7 +53,7 @@ impl TaskHeader { #[cfg(feature = "time")] expires_at: Cell::new(Instant::from_ticks(0)), #[cfg(feature = "time")] - timer_queue_item: TimerQueueItem::new(), + timer_queue_item: timer_queue::TimerQueueItem::new(), } } @@ -105,20 +108,14 @@ impl Task { } } - SpawnToken { - raw_task: None, - phantom: PhantomData, - } + SpawnToken::new_failed() } pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { if self.spawn_allocate() { unsafe { self.spawn_initialize(future) } } else { - SpawnToken { - raw_task: None, - phantom: PhantomData, - } + SpawnToken::new_failed() } } @@ -135,10 +132,7 @@ impl Task { self.raw.poll_fn.write(Self::poll); self.future.write(future()); - return SpawnToken { - raw_task: Some(NonNull::new_unchecked(&self.raw as *const TaskHeader as _)), - phantom: PhantomData, - }; + SpawnToken::new(NonNull::new_unchecked(&self.raw as *const TaskHeader as _)) } unsafe fn poll(p: NonNull) { @@ -169,7 +163,7 @@ pub struct Executor { signal_ctx: *mut (), #[cfg(feature = "time")] - timer_queue: TimerQueue, + pub(crate) timer_queue: timer_queue::TimerQueue, #[cfg(feature = "time")] alarm: AlarmHandle, } @@ -187,7 +181,7 @@ impl Executor { signal_ctx, #[cfg(feature = "time")] - timer_queue: TimerQueue::new(), + timer_queue: timer_queue::TimerQueue::new(), #[cfg(feature = "time")] alarm, } @@ -249,15 +243,10 @@ impl Executor { } pub unsafe fn spawner(&'static self) -> super::Spawner { - super::Spawner { - executor: self, - not_send: PhantomData, - } + super::Spawner::new(self) } } -pub use super::waker::task_from_waker; - pub unsafe fn wake_task(task: NonNull) { task.as_ref().enqueue(); } diff --git a/embassy/src/executor/run_queue.rs b/embassy/src/executor/raw/run_queue.rs similarity index 98% rename from embassy/src/executor/run_queue.rs rename to embassy/src/executor/raw/run_queue.rs index 08391613..8e8bc8ff 100644 --- a/embassy/src/executor/run_queue.rs +++ b/embassy/src/executor/raw/run_queue.rs @@ -2,7 +2,7 @@ use atomic_polyfill::{AtomicPtr, Ordering}; use core::ptr; use core::ptr::NonNull; -use super::raw::TaskHeader; +use super::TaskHeader; pub(crate) struct RunQueueItem { next: AtomicPtr, diff --git a/embassy/src/executor/timer_queue.rs b/embassy/src/executor/raw/timer_queue.rs similarity index 97% rename from embassy/src/executor/timer_queue.rs rename to embassy/src/executor/raw/timer_queue.rs index 76bc27ad..e96910bb 100644 --- a/embassy/src/executor/timer_queue.rs +++ b/embassy/src/executor/raw/timer_queue.rs @@ -4,7 +4,7 @@ use core::cmp::min; use core::ptr; use core::ptr::NonNull; -use super::raw::{TaskHeader, STATE_TIMER_QUEUED}; +use super::{TaskHeader, STATE_TIMER_QUEUED}; use crate::time::Instant; pub(crate) struct TimerQueueItem { diff --git a/embassy/src/executor/util.rs b/embassy/src/executor/raw/util.rs similarity index 100% rename from embassy/src/executor/util.rs rename to embassy/src/executor/raw/util.rs diff --git a/embassy/src/executor/waker.rs b/embassy/src/executor/raw/waker.rs similarity index 97% rename from embassy/src/executor/waker.rs rename to embassy/src/executor/raw/waker.rs index ea5b501f..e53190f1 100644 --- a/embassy/src/executor/waker.rs +++ b/embassy/src/executor/raw/waker.rs @@ -2,7 +2,7 @@ use core::mem; use core::ptr::NonNull; use core::task::{RawWaker, RawWakerVTable, Waker}; -use super::raw::TaskHeader; +use super::TaskHeader; const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop); diff --git a/embassy/src/executor/spawner.rs b/embassy/src/executor/spawner.rs new file mode 100644 index 00000000..36100aec --- /dev/null +++ b/embassy/src/executor/spawner.rs @@ -0,0 +1,128 @@ +use core::marker::PhantomData; +use core::mem; +use core::ptr::NonNull; + +use super::raw; + +#[must_use = "Calling a task function does nothing on its own. You must pass the returned SpawnToken to Executor::spawn()"] +pub struct SpawnToken { + raw_task: Option>, + phantom: PhantomData<*mut F>, +} + +impl SpawnToken { + pub(crate) unsafe fn new(raw_task: NonNull) -> Self { + Self { + raw_task: Some(raw_task), + phantom: PhantomData, + } + } + + pub(crate) fn new_failed() -> Self { + Self { + raw_task: None, + phantom: PhantomData, + } + } +} + +impl Drop for SpawnToken { + fn drop(&mut self) { + // TODO deallocate the task instead. + panic!("SpawnToken instances may not be dropped. You must pass them to Executor::spawn()") + } +} + +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SpawnError { + Busy, +} + +/// Handle to spawn tasks into an executor. +/// +/// This Spawner can spawn any task (Send and non-Send ones), but it can +/// only be used in the executor thread (it is not Send itself). +/// +/// If you want to spawn tasks from another thread, use [SendSpawner]. +#[derive(Copy, Clone)] +pub struct Spawner { + executor: &'static raw::Executor, + not_send: PhantomData<*mut ()>, +} + +impl Spawner { + pub(crate) unsafe fn new(executor: &'static raw::Executor) -> Self { + Self { + executor, + not_send: PhantomData, + } + } + + pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { + let task = token.raw_task; + mem::forget(token); + + match task { + Some(task) => { + unsafe { self.executor.spawn(task) }; + Ok(()) + } + None => Err(SpawnError::Busy), + } + } + + /// Used by the `embassy_macros::main!` macro to throw an error when spawn + /// fails. This is here to allow conditional use of `defmt::unwrap!` + /// without introducing a `defmt` feature in the `embassy_macros` package, + /// which would require use of `-Z namespaced-features`. + pub fn must_spawn(&self, token: SpawnToken) -> () { + unwrap!(self.spawn(token)); + } + + /// Convert this Spawner to a SendSpawner. This allows you to send the + /// spawner to other threads, but the spawner loses the ability to spawn + /// non-Send tasks. + pub fn make_send(&self) -> SendSpawner { + SendSpawner { + executor: self.executor, + not_send: PhantomData, + } + } +} + +/// Handle to spawn tasks into an executor from any thread. +/// +/// This Spawner can be used from any thread (it implements Send and Sync, so after any task (Send and non-Send ones), but it can +/// only be used in the executor thread (it is not Send itself). +/// +/// If you want to spawn tasks from another thread, use [SendSpawner]. +#[derive(Copy, Clone)] +pub struct SendSpawner { + executor: &'static raw::Executor, + not_send: PhantomData<*mut ()>, +} + +unsafe impl Send for SendSpawner {} +unsafe impl Sync for SendSpawner {} + +/// Handle to spawn tasks to an executor. +/// +/// This Spawner can spawn any task (Send and non-Send ones), but it can +/// only be used in the executor thread (it is not Send itself). +/// +/// If you want to spawn tasks from another thread, use [SendSpawner]. +impl SendSpawner { + pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { + let header = token.raw_task; + mem::forget(token); + + match header { + Some(header) => { + unsafe { self.executor.spawn(header) }; + Ok(()) + } + None => Err(SpawnError::Busy), + } + } +}