Merge pull request #372 from embassy-rs/executor-structure
executor: improve module structure
This commit is contained in:
commit
09ffdf63f1
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;
|
#[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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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,14 +243,9 @@ 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();
|
@ -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>,
|
@ -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 {
|
@ -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);
|
||||||
|
|
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 New Issue
Block a user