2021-08-25 00:20:29 +02:00
|
|
|
use core::marker::PhantomData;
|
|
|
|
use core::mem;
|
|
|
|
use core::ptr::NonNull;
|
|
|
|
|
|
|
|
use super::raw;
|
|
|
|
|
2021-08-26 00:20:52 +02:00
|
|
|
/// Token to spawn a newly-created task in an executor.
|
|
|
|
///
|
|
|
|
/// When calling a task function (like `#[embassy::task] async fn my_task() { ... }`), the returned
|
|
|
|
/// value is a `SpawnToken` that represents an instance of the task, ready to spawn. You must
|
|
|
|
/// then spawn it into an executor, typically with [`Spawner::spawn()`].
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// Dropping a SpawnToken instance panics. You may not "abort" spawning a task in this way.
|
|
|
|
/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it.
|
|
|
|
#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"]
|
2021-08-25 00:20:29 +02:00
|
|
|
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.
|
2021-08-26 00:20:52 +02:00
|
|
|
panic!("SpawnToken instances may not be dropped. You must pass them to Spawner::spawn()")
|
2021-08-25 00:20:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-26 00:20:52 +02:00
|
|
|
/// Error returned when spawning a task.
|
2021-08-25 00:20:29 +02:00
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
|
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
|
|
|
pub enum SpawnError {
|
2021-08-26 00:20:52 +02:00
|
|
|
/// Too many instances of this task are already running.
|
|
|
|
///
|
|
|
|
/// By default, a task marked with `#[embassy::task]` can only have one instance
|
|
|
|
/// running at a time. You may allow multiple instances to run in parallel with
|
|
|
|
/// `#[embassy::task(pool_size = 4)]`, at the cost of higher RAM usage.
|
2021-08-25 00:20:29 +02:00
|
|
|
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 {
|
2021-08-26 00:20:52 +02:00
|
|
|
pub(crate) fn new(executor: &'static raw::Executor) -> Self {
|
2021-08-25 00:20:29 +02:00
|
|
|
Self {
|
|
|
|
executor,
|
|
|
|
not_send: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-26 00:20:52 +02:00
|
|
|
/// Spawn a task into an executor.
|
|
|
|
///
|
2021-11-02 18:49:01 +01:00
|
|
|
/// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy::task]`).
|
2021-08-25 00:20:29 +02:00
|
|
|
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`.
|
2021-10-18 00:55:43 +02:00
|
|
|
pub fn must_spawn<F>(&self, token: SpawnToken<F>) {
|
2021-08-25 00:20:29 +02:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Handle to spawn tasks into an executor from any thread.
|
|
|
|
///
|
2021-08-26 00:20:52 +02:00
|
|
|
/// This Spawner can be used from any thread (it is Send), but it can
|
|
|
|
/// only spawn Send tasks. The reason for this is spawning is effectively
|
|
|
|
/// "sending" the tasks to the executor thread.
|
2021-08-25 00:20:29 +02:00
|
|
|
///
|
2021-08-26 00:20:52 +02:00
|
|
|
/// If you want to spawn non-Send tasks, use [Spawner].
|
2021-08-25 00:20:29 +02:00
|
|
|
#[derive(Copy, Clone)]
|
|
|
|
pub struct SendSpawner {
|
|
|
|
executor: &'static raw::Executor,
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe impl Send for SendSpawner {}
|
|
|
|
unsafe impl Sync for SendSpawner {}
|
|
|
|
|
|
|
|
impl SendSpawner {
|
2021-08-26 00:20:52 +02:00
|
|
|
/// Spawn a task into an executor.
|
|
|
|
///
|
2021-11-02 18:49:01 +01:00
|
|
|
/// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy::task]`).
|
2021-08-25 00:20:29 +02:00
|
|
|
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),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|