Merge pull request #372 from embassy-rs/executor-structure
executor: improve module structure
This commit is contained in:
commit
09ffdf63f1
8 changed files with 230 additions and 213 deletions
76
embassy/src/executor/arch/arm.rs
Normal file
76
embassy/src/executor/arch/arm.rs
Normal file
|
@ -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<I: Interrupt> {
|
||||
irq: I,
|
||||
inner: raw::Executor,
|
||||
not_send: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl<I: Interrupt> InterruptExecutor<I> {
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -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<F> {
|
||||
raw_task: Option<NonNull<raw::TaskHeader>>,
|
||||
phantom: PhantomData<*mut F>,
|
||||
}
|
||||
|
||||
impl<F> Drop for SpawnToken<F> {
|
||||
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<F>(&self, token: SpawnToken<F>) -> 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<F>(&self, token: SpawnToken<F>) -> () {
|
||||
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<F: Send>(&self, token: SpawnToken<F>) -> 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<I: Interrupt> {
|
||||
irq: I,
|
||||
inner: raw::Executor,
|
||||
not_send: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl<I: Interrupt> InterruptExecutor<I> {
|
||||
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::*;
|
||||
|
|
|
@ -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<Instant>,
|
||||
#[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<F: Future + 'static> Task<F> {
|
|||
}
|
||||
}
|
||||
|
||||
SpawnToken {
|
||||
raw_task: None,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
SpawnToken::new_failed()
|
||||
}
|
||||
|
||||
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<F> {
|
||||
if self.spawn_allocate() {
|
||||
unsafe { self.spawn_initialize(future) }
|
||||
} else {
|
||||
SpawnToken {
|
||||
raw_task: None,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
SpawnToken::new_failed()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,10 +132,7 @@ impl<F: Future + 'static> Task<F> {
|
|||
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<TaskHeader>) {
|
||||
|
@ -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<TaskHeader>) {
|
||||
task.as_ref().enqueue();
|
||||
}
|
|
@ -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<TaskHeader>,
|
|
@ -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 {
|
|
@ -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);
|
||||
|
128
embassy/src/executor/spawner.rs
Normal file
128
embassy/src/executor/spawner.rs
Normal file
|
@ -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<F> {
|
||||
raw_task: Option<NonNull<raw::TaskHeader>>,
|
||||
phantom: PhantomData<*mut F>,
|
||||
}
|
||||
|
||||
impl<F> SpawnToken<F> {
|
||||
pub(crate) unsafe fn new(raw_task: NonNull<raw::TaskHeader>) -> Self {
|
||||
Self {
|
||||
raw_task: Some(raw_task),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_failed() -> Self {
|
||||
Self {
|
||||
raw_task: None,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Drop for SpawnToken<F> {
|
||||
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<F>(&self, token: SpawnToken<F>) -> 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<F>(&self, token: SpawnToken<F>) -> () {
|
||||
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<F: Send>(&self, token: SpawnToken<F>) -> Result<(), SpawnError> {
|
||||
let header = token.raw_task;
|
||||
mem::forget(token);
|
||||
|
||||
match header {
|
||||
Some(header) => {
|
||||
unsafe { self.executor.spawn(header) };
|
||||
Ok(())
|
||||
}
|
||||
None => Err(SpawnError::Busy),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue