embassy_executor/
spawner.rs

1use core::future::{poll_fn, Future};
2use core::marker::PhantomData;
3use core::mem;
4use core::sync::atomic::Ordering;
5use core::task::Poll;
6
7use super::raw;
8
9/// Token to spawn a newly-created task in an executor.
10///
11/// When calling a task function (like `#[embassy_executor::task] async fn my_task() { ... }`), the returned
12/// value is a `SpawnToken` that represents an instance of the task, ready to spawn. You must
13/// then spawn it into an executor, typically with [`Spawner::spawn()`].
14///
15/// The generic parameter `S` determines whether the task can be spawned in executors
16/// in other threads or not. If `S: Send`, it can, which allows spawning it into a [`SendSpawner`].
17/// If not, it can't, so it can only be spawned into the current thread's executor, with [`Spawner`].
18///
19/// # Panics
20///
21/// Dropping a SpawnToken instance panics. You may not "abort" spawning a task in this way.
22/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it.
23#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"]
24pub struct SpawnToken<S> {
25    raw_task: Option<raw::TaskRef>,
26    phantom: PhantomData<*mut S>,
27}
28
29impl<S> SpawnToken<S> {
30    pub(crate) unsafe fn new(raw_task: raw::TaskRef) -> Self {
31        Self {
32            raw_task: Some(raw_task),
33            phantom: PhantomData,
34        }
35    }
36
37    /// Return a SpawnToken that represents a failed spawn.
38    pub fn new_failed() -> Self {
39        Self {
40            raw_task: None,
41            phantom: PhantomData,
42        }
43    }
44}
45
46impl<S> Drop for SpawnToken<S> {
47    fn drop(&mut self) {
48        // TODO deallocate the task instead.
49        panic!("SpawnToken instances may not be dropped. You must pass them to Spawner::spawn()")
50    }
51}
52
53/// Error returned when spawning a task.
54#[derive(Copy, Clone, Debug)]
55#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56pub enum SpawnError {
57    /// Too many instances of this task are already running.
58    ///
59    /// By default, a task marked with `#[embassy_executor::task]` can only have one instance
60    /// running at a time. You may allow multiple instances to run in parallel with
61    /// `#[embassy_executor::task(pool_size = 4)]`, at the cost of higher RAM usage.
62    Busy,
63}
64
65impl core::fmt::Display for SpawnError {
66    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
67        match self {
68            SpawnError::Busy => write!(f, "Busy"),
69        }
70    }
71}
72
73impl core::error::Error for SpawnError {}
74
75/// Handle to spawn tasks into an executor.
76///
77/// This Spawner can spawn any task (Send and non-Send ones), but it can
78/// only be used in the executor thread (it is not Send itself).
79///
80/// If you want to spawn tasks from another thread, use [SendSpawner].
81#[derive(Copy, Clone)]
82pub struct Spawner {
83    executor: &'static raw::Executor,
84    not_send: PhantomData<*mut ()>,
85}
86
87impl Spawner {
88    pub(crate) fn new(executor: &'static raw::Executor) -> Self {
89        Self {
90            executor,
91            not_send: PhantomData,
92        }
93    }
94
95    /// Get a Spawner for the current executor.
96    ///
97    /// This function is `async` just to get access to the current async
98    /// context. It returns instantly, it does not block/yield.
99    ///
100    /// # Panics
101    ///
102    /// Panics if the current executor is not an Embassy executor.
103    pub fn for_current_executor() -> impl Future<Output = Self> {
104        poll_fn(|cx| {
105            let task = raw::task_from_waker(cx.waker());
106            let executor = unsafe {
107                task.header()
108                    .executor
109                    .load(Ordering::Relaxed)
110                    .as_ref()
111                    .unwrap_unchecked()
112            };
113            let executor = unsafe { raw::Executor::wrap(executor) };
114            Poll::Ready(Self::new(executor))
115        })
116    }
117
118    /// Spawn a task into an executor.
119    ///
120    /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`).
121    pub fn spawn<S>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> {
122        let task = token.raw_task;
123        mem::forget(token);
124
125        match task {
126            Some(task) => {
127                unsafe { self.executor.spawn(task) };
128                Ok(())
129            }
130            None => Err(SpawnError::Busy),
131        }
132    }
133
134    // Used by the `embassy_executor_macros::main!` macro to throw an error when spawn
135    // fails. This is here to allow conditional use of `defmt::unwrap!`
136    // without introducing a `defmt` feature in the `embassy_executor_macros` package,
137    // which would require use of `-Z namespaced-features`.
138    /// Spawn a task into an executor, panicking on failure.
139    ///
140    /// # Panics
141    ///
142    /// Panics if the spawning fails.
143    pub fn must_spawn<S>(&self, token: SpawnToken<S>) {
144        unwrap!(self.spawn(token));
145    }
146
147    /// Convert this Spawner to a SendSpawner. This allows you to send the
148    /// spawner to other threads, but the spawner loses the ability to spawn
149    /// non-Send tasks.
150    pub fn make_send(&self) -> SendSpawner {
151        SendSpawner::new(&self.executor.inner)
152    }
153}
154
155/// Handle to spawn tasks into an executor from any thread.
156///
157/// This Spawner can be used from any thread (it is Send), but it can
158/// only spawn Send tasks. The reason for this is spawning is effectively
159/// "sending" the tasks to the executor thread.
160///
161/// If you want to spawn non-Send tasks, use [Spawner].
162#[derive(Copy, Clone)]
163pub struct SendSpawner {
164    executor: &'static raw::SyncExecutor,
165}
166
167impl SendSpawner {
168    pub(crate) fn new(executor: &'static raw::SyncExecutor) -> Self {
169        Self { executor }
170    }
171
172    /// Get a Spawner for the current executor.
173    ///
174    /// This function is `async` just to get access to the current async
175    /// context. It returns instantly, it does not block/yield.
176    ///
177    /// # Panics
178    ///
179    /// Panics if the current executor is not an Embassy executor.
180    pub fn for_current_executor() -> impl Future<Output = Self> {
181        poll_fn(|cx| {
182            let task = raw::task_from_waker(cx.waker());
183            let executor = unsafe {
184                task.header()
185                    .executor
186                    .load(Ordering::Relaxed)
187                    .as_ref()
188                    .unwrap_unchecked()
189            };
190            Poll::Ready(Self::new(executor))
191        })
192    }
193
194    /// Spawn a task into an executor.
195    ///
196    /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`).
197    pub fn spawn<S: Send>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> {
198        let header = token.raw_task;
199        mem::forget(token);
200
201        match header {
202            Some(header) => {
203                unsafe { self.executor.spawn(header) };
204                Ok(())
205            }
206            None => Err(SpawnError::Busy),
207        }
208    }
209
210    /// Spawn a task into an executor, panicking on failure.
211    ///
212    /// # Panics
213    ///
214    /// Panics if the spawning fails.
215    pub fn must_spawn<S: Send>(&self, token: SpawnToken<S>) {
216        unwrap!(self.spawn(token));
217    }
218}