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#[cfg(feature = "trace")]
9use crate::raw::trace::TaskRefTrace;
10
11/// Token to spawn a newly-created task in an executor.
12///
13/// When calling a task function (like `#[embassy_executor::task] async fn my_task() { ... }`), the returned
14/// value is a `SpawnToken` that represents an instance of the task, ready to spawn. You must
15/// then spawn it into an executor, typically with [`Spawner::spawn()`].
16///
17/// The generic parameter `S` determines whether the task can be spawned in executors
18/// in other threads or not. If `S: Send`, it can, which allows spawning it into a [`SendSpawner`].
19/// If not, it can't, so it can only be spawned into the current thread's executor, with [`Spawner`].
20///
21/// # Panics
22///
23/// Dropping a SpawnToken instance panics. You may not "abort" spawning a task in this way.
24/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it.
25#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"]
26pub struct SpawnToken<S> {
27    pub(crate) raw_task: Option<raw::TaskRef>,
28    phantom: PhantomData<*mut S>,
29}
30
31impl<S> SpawnToken<S> {
32    pub(crate) unsafe fn new(raw_task: raw::TaskRef) -> Self {
33        Self {
34            raw_task: Some(raw_task),
35            phantom: PhantomData,
36        }
37    }
38
39    /// Returns the task id if available, otherwise 0
40    /// This can be used in combination with rtos-trace to match task names with id's
41    pub fn id(&self) -> u32 {
42        match self.raw_task {
43            None => 0,
44            Some(t) => t.as_ptr() as u32,
45        }
46    }
47
48    /// Return a SpawnToken that represents a failed spawn.
49    pub fn new_failed() -> Self {
50        Self {
51            raw_task: None,
52            phantom: PhantomData,
53        }
54    }
55}
56
57impl<S> Drop for SpawnToken<S> {
58    fn drop(&mut self) {
59        // TODO deallocate the task instead.
60        panic!("SpawnToken instances may not be dropped. You must pass them to Spawner::spawn()")
61    }
62}
63
64/// Error returned when spawning a task.
65#[derive(Copy, Clone)]
66pub enum SpawnError {
67    /// Too many instances of this task are already running.
68    ///
69    /// By default, a task marked with `#[embassy_executor::task]` can only have one instance
70    /// running at a time. You may allow multiple instances to run in parallel with
71    /// `#[embassy_executor::task(pool_size = 4)]`, at the cost of higher RAM usage.
72    Busy,
73}
74
75impl core::fmt::Debug for SpawnError {
76    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
77        core::fmt::Display::fmt(self, f)
78    }
79}
80
81impl core::fmt::Display for SpawnError {
82    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
83        match self {
84            SpawnError::Busy => write!(f, "Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task."),
85        }
86    }
87}
88
89#[cfg(feature = "defmt")]
90impl defmt::Format for SpawnError {
91    fn format(&self, f: defmt::Formatter) {
92        match self {
93            SpawnError::Busy => defmt::write!(f, "Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task."),
94        }
95    }
96}
97
98impl core::error::Error for SpawnError {}
99
100/// Handle to spawn tasks into an executor.
101///
102/// This Spawner can spawn any task (Send and non-Send ones), but it can
103/// only be used in the executor thread (it is not Send itself).
104///
105/// If you want to spawn tasks from another thread, use [SendSpawner].
106#[derive(Copy, Clone)]
107pub struct Spawner {
108    pub(crate) executor: &'static raw::Executor,
109    not_send: PhantomData<*mut ()>,
110}
111
112impl Spawner {
113    pub(crate) fn new(executor: &'static raw::Executor) -> Self {
114        Self {
115            executor,
116            not_send: PhantomData,
117        }
118    }
119
120    /// Get a Spawner for the current executor.
121    ///
122    /// This function is `async` just to get access to the current async
123    /// context. It returns instantly, it does not block/yield.
124    ///
125    /// Using this method is discouraged due to it being unsafe. Consider the following
126    /// alternatives instead:
127    ///
128    /// - Pass the initial `Spawner` as an argument to tasks. Note that it's `Copy`, so you can
129    ///   make as many copies of it as you want.
130    /// - Use `SendSpawner::for_current_executor()` instead, which is safe but can only be used
131    ///   if task arguments are `Send`.
132    ///
133    /// The only case where using this method is absolutely required is obtaining the `Spawner`
134    /// for an `InterruptExecutor`.
135    ///
136    /// # Safety
137    ///
138    /// You must only execute this with an async `Context` created by the Embassy executor.
139    /// You must not execute it with manually-created `Context`s.
140    ///
141    /// # Panics
142    ///
143    /// Panics if the current executor is not an Embassy executor.
144    pub unsafe fn for_current_executor() -> impl Future<Output = Self> {
145        poll_fn(|cx| {
146            let task = raw::task_from_waker(cx.waker());
147            let executor = unsafe {
148                task.header()
149                    .executor
150                    .load(Ordering::Relaxed)
151                    .as_ref()
152                    .unwrap_unchecked()
153            };
154            let executor = unsafe { raw::Executor::wrap(executor) };
155            Poll::Ready(Self::new(executor))
156        })
157    }
158
159    /// Spawn a task into an executor.
160    ///
161    /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`).
162    pub fn spawn<S>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> {
163        let task = token.raw_task;
164        mem::forget(token);
165
166        match task {
167            Some(task) => {
168                unsafe { self.executor.spawn(task) };
169                Ok(())
170            }
171            None => Err(SpawnError::Busy),
172        }
173    }
174
175    // Used by the `embassy_executor_macros::main!` macro to throw an error when spawn
176    // fails. This is here to allow conditional use of `defmt::unwrap!`
177    // without introducing a `defmt` feature in the `embassy_executor_macros` package,
178    // which would require use of `-Z namespaced-features`.
179    /// Spawn a task into an executor, panicking on failure.
180    ///
181    /// # Panics
182    ///
183    /// Panics if the spawning fails.
184    pub fn must_spawn<S>(&self, token: SpawnToken<S>) {
185        unwrap!(self.spawn(token));
186    }
187
188    /// Convert this Spawner to a SendSpawner. This allows you to send the
189    /// spawner to other threads, but the spawner loses the ability to spawn
190    /// non-Send tasks.
191    pub fn make_send(&self) -> SendSpawner {
192        SendSpawner::new(&self.executor.inner)
193    }
194
195    /// Return the unique ID of this Spawner's Executor.
196    pub fn executor_id(&self) -> usize {
197        self.executor.id()
198    }
199}
200
201/// Extension trait adding tracing capabilities to the Spawner
202///
203/// This trait provides an additional method to spawn tasks with an associated name,
204/// which can be useful for debugging and tracing purposes.
205pub trait SpawnerTraceExt {
206    /// Spawns a new task with a specified name.
207    ///
208    /// # Arguments
209    /// * `name` - Static string name to associate with the task
210    /// * `token` - Token representing the task to spawn
211    ///
212    /// # Returns
213    /// Result indicating whether the spawn was successful
214    fn spawn_named<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError>;
215}
216
217/// Implementation of the SpawnerTraceExt trait for Spawner when trace is enabled
218#[cfg(feature = "trace")]
219impl SpawnerTraceExt for Spawner {
220    fn spawn_named<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> {
221        let task = token.raw_task;
222        core::mem::forget(token);
223
224        match task {
225            Some(task) => {
226                // Set the name and ID when trace is enabled
227                task.set_name(Some(name));
228                let task_id = task.as_ptr() as u32;
229                task.set_id(task_id);
230
231                unsafe { self.executor.spawn(task) };
232                Ok(())
233            }
234            None => Err(SpawnError::Busy),
235        }
236    }
237}
238
239/// Implementation of the SpawnerTraceExt trait for Spawner when trace is disabled
240#[cfg(not(feature = "trace"))]
241impl SpawnerTraceExt for Spawner {
242    fn spawn_named<S>(&self, _name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> {
243        // When trace is disabled, just forward to regular spawn and ignore the name
244        self.spawn(token)
245    }
246}
247
248/// Handle to spawn tasks into an executor from any thread.
249///
250/// This Spawner can be used from any thread (it is Send), but it can
251/// only spawn Send tasks. The reason for this is spawning is effectively
252/// "sending" the tasks to the executor thread.
253///
254/// If you want to spawn non-Send tasks, use [Spawner].
255#[derive(Copy, Clone)]
256pub struct SendSpawner {
257    executor: &'static raw::SyncExecutor,
258}
259
260impl SendSpawner {
261    pub(crate) fn new(executor: &'static raw::SyncExecutor) -> Self {
262        Self { executor }
263    }
264
265    /// Get a Spawner for the current executor.
266    ///
267    /// This function is `async` just to get access to the current async
268    /// context. It returns instantly, it does not block/yield.
269    ///
270    /// # Panics
271    ///
272    /// Panics if the current executor is not an Embassy executor.
273    pub fn for_current_executor() -> impl Future<Output = Self> {
274        poll_fn(|cx| {
275            let task = raw::task_from_waker(cx.waker());
276            let executor = unsafe {
277                task.header()
278                    .executor
279                    .load(Ordering::Relaxed)
280                    .as_ref()
281                    .unwrap_unchecked()
282            };
283            Poll::Ready(Self::new(executor))
284        })
285    }
286
287    /// Spawn a task into an executor.
288    ///
289    /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`).
290    pub fn spawn<S: Send>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> {
291        let header = token.raw_task;
292        mem::forget(token);
293
294        match header {
295            Some(header) => {
296                unsafe { self.executor.spawn(header) };
297                Ok(())
298            }
299            None => Err(SpawnError::Busy),
300        }
301    }
302
303    /// Spawn a task into an executor, panicking on failure.
304    ///
305    /// # Panics
306    ///
307    /// Panics if the spawning fails.
308    pub fn must_spawn<S: Send>(&self, token: SpawnToken<S>) {
309        unwrap!(self.spawn(token));
310    }
311}