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}