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