Merge pull request #372 from embassy-rs/executor-structure

executor: improve module structure
This commit is contained in:
Dario Nieuwenhuis 2021-08-25 00:27:21 +02:00 committed by GitHub
commit 09ffdf63f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 230 additions and 213 deletions

View 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();
}
}

View File

@ -1,183 +1,7 @@
use core::marker::PhantomData; #[path = "arch/arm.rs"]
use core::ptr::NonNull; mod arch;
use core::{mem, ptr};
pub mod raw; pub mod raw;
mod run_queue; mod spawner;
#[cfg(feature = "time")]
mod timer_queue;
mod util;
mod waker;
use crate::interrupt::{Interrupt, InterruptExt}; pub use arch::*;
pub use spawner::*;
#[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();
}
}

View File

@ -1,24 +1,27 @@
mod run_queue;
#[cfg(feature = "time")]
mod timer_queue;
mod util;
mod waker;
use atomic_polyfill::{AtomicU32, Ordering}; use atomic_polyfill::{AtomicU32, Ordering};
use core::cell::Cell; use core::cell::Cell;
use core::future::Future; use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin; use core::pin::Pin;
use core::ptr::NonNull; use core::ptr::NonNull;
use core::task::{Context, Poll}; use core::task::{Context, Poll};
use core::{mem, ptr}; use core::{mem, ptr};
use super::run_queue::{RunQueue, RunQueueItem}; use self::run_queue::{RunQueue, RunQueueItem};
use super::util::UninitCell; use self::util::UninitCell;
use super::waker;
use super::SpawnToken; use super::SpawnToken;
#[cfg(feature = "time")]
use super::timer_queue::{TimerQueue, TimerQueueItem};
#[cfg(feature = "time")] #[cfg(feature = "time")]
use crate::time::driver::{self, AlarmHandle}; use crate::time::driver::{self, AlarmHandle};
#[cfg(feature = "time")] #[cfg(feature = "time")]
use crate::time::Instant; use crate::time::Instant;
pub use self::waker::task_from_waker;
/// Task is spawned (has a future) /// Task is spawned (has a future)
pub(crate) const STATE_SPAWNED: u32 = 1 << 0; pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
/// Task is in the executor run queue /// Task is in the executor run queue
@ -36,7 +39,7 @@ pub struct TaskHeader {
#[cfg(feature = "time")] #[cfg(feature = "time")]
pub(crate) expires_at: Cell<Instant>, pub(crate) expires_at: Cell<Instant>,
#[cfg(feature = "time")] #[cfg(feature = "time")]
pub(crate) timer_queue_item: TimerQueueItem, pub(crate) timer_queue_item: timer_queue::TimerQueueItem,
} }
impl TaskHeader { impl TaskHeader {
@ -50,7 +53,7 @@ impl TaskHeader {
#[cfg(feature = "time")] #[cfg(feature = "time")]
expires_at: Cell::new(Instant::from_ticks(0)), expires_at: Cell::new(Instant::from_ticks(0)),
#[cfg(feature = "time")] #[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 { SpawnToken::new_failed()
raw_task: None,
phantom: PhantomData,
}
} }
pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<F> { pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<F> {
if self.spawn_allocate() { if self.spawn_allocate() {
unsafe { self.spawn_initialize(future) } unsafe { self.spawn_initialize(future) }
} else { } else {
SpawnToken { SpawnToken::new_failed()
raw_task: None,
phantom: PhantomData,
}
} }
} }
@ -135,10 +132,7 @@ impl<F: Future + 'static> Task<F> {
self.raw.poll_fn.write(Self::poll); self.raw.poll_fn.write(Self::poll);
self.future.write(future()); self.future.write(future());
return SpawnToken { SpawnToken::new(NonNull::new_unchecked(&self.raw as *const TaskHeader as _))
raw_task: Some(NonNull::new_unchecked(&self.raw as *const TaskHeader as _)),
phantom: PhantomData,
};
} }
unsafe fn poll(p: NonNull<TaskHeader>) { unsafe fn poll(p: NonNull<TaskHeader>) {
@ -169,7 +163,7 @@ pub struct Executor {
signal_ctx: *mut (), signal_ctx: *mut (),
#[cfg(feature = "time")] #[cfg(feature = "time")]
timer_queue: TimerQueue, pub(crate) timer_queue: timer_queue::TimerQueue,
#[cfg(feature = "time")] #[cfg(feature = "time")]
alarm: AlarmHandle, alarm: AlarmHandle,
} }
@ -187,7 +181,7 @@ impl Executor {
signal_ctx, signal_ctx,
#[cfg(feature = "time")] #[cfg(feature = "time")]
timer_queue: TimerQueue::new(), timer_queue: timer_queue::TimerQueue::new(),
#[cfg(feature = "time")] #[cfg(feature = "time")]
alarm, alarm,
} }
@ -249,15 +243,10 @@ impl Executor {
} }
pub unsafe fn spawner(&'static self) -> super::Spawner { pub unsafe fn spawner(&'static self) -> super::Spawner {
super::Spawner { super::Spawner::new(self)
executor: self,
not_send: PhantomData,
}
} }
} }
pub use super::waker::task_from_waker;
pub unsafe fn wake_task(task: NonNull<TaskHeader>) { pub unsafe fn wake_task(task: NonNull<TaskHeader>) {
task.as_ref().enqueue(); task.as_ref().enqueue();
} }

View File

@ -2,7 +2,7 @@ use atomic_polyfill::{AtomicPtr, Ordering};
use core::ptr; use core::ptr;
use core::ptr::NonNull; use core::ptr::NonNull;
use super::raw::TaskHeader; use super::TaskHeader;
pub(crate) struct RunQueueItem { pub(crate) struct RunQueueItem {
next: AtomicPtr<TaskHeader>, next: AtomicPtr<TaskHeader>,

View File

@ -4,7 +4,7 @@ use core::cmp::min;
use core::ptr; use core::ptr;
use core::ptr::NonNull; use core::ptr::NonNull;
use super::raw::{TaskHeader, STATE_TIMER_QUEUED}; use super::{TaskHeader, STATE_TIMER_QUEUED};
use crate::time::Instant; use crate::time::Instant;
pub(crate) struct TimerQueueItem { pub(crate) struct TimerQueueItem {

View File

@ -2,7 +2,7 @@ use core::mem;
use core::ptr::NonNull; use core::ptr::NonNull;
use core::task::{RawWaker, RawWakerVTable, Waker}; use core::task::{RawWaker, RawWakerVTable, Waker};
use super::raw::TaskHeader; use super::TaskHeader;
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop); const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);

View 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),
}
}
}