embassy_executor/spawner.rs
1use core::future::{Future, poll_fn};
2use core::marker::PhantomData;
3use core::mem;
4use core::sync::atomic::Ordering;
5use core::task::Poll;
6
7use super::raw;
8use crate::Metadata;
9
10/// Token to spawn a newly-created task in an executor.
11///
12/// When calling a task function (like `#[embassy_executor::task] async fn my_task() { ... }`), the returned
13/// value is a `SpawnToken` that represents an instance of the task, ready to spawn. You must
14/// then spawn it into an executor, typically with [`Spawner::spawn()`].
15///
16/// The generic parameter `S` determines whether the task can be spawned in executors
17/// in other threads or not. If `S: Send`, it can, which allows spawning it into a [`SendSpawner`].
18/// If not, it can't, so it can only be spawned into the current thread's executor, with [`Spawner`].
19///
20/// # Panics
21///
22/// Dropping a SpawnToken instance panics. You may not "abort" spawning a task in this way.
23/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it.
24#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"]
25pub struct SpawnToken<S> {
26 pub(crate) raw_task: raw::TaskRef,
27 phantom: PhantomData<*mut S>,
28}
29
30impl<S> SpawnToken<S> {
31 pub(crate) unsafe fn new(raw_task: raw::TaskRef) -> Self {
32 Self {
33 raw_task,
34 phantom: PhantomData,
35 }
36 }
37
38 /// Returns the task ID.
39 /// This can be used in combination with rtos-trace to match task names with IDs
40 pub fn id(&self) -> u32 {
41 self.raw_task.id()
42 }
43
44 /// Get the metadata for this task. You can use this to set metadata fields
45 /// prior to spawning it.
46 pub fn metadata(&self) -> &Metadata {
47 self.raw_task.metadata()
48 }
49}
50
51impl<S> Drop for SpawnToken<S> {
52 fn drop(&mut self) {
53 // TODO deallocate the task instead.
54 panic!("SpawnToken instances may not be dropped. You must pass them to Spawner::spawn()")
55 }
56}
57
58/// Error returned when spawning a task.
59#[derive(Copy, Clone)]
60pub enum SpawnError {
61 /// Too many instances of this task are already running.
62 ///
63 /// By default, a task marked with `#[embassy_executor::task]` can only have one instance
64 /// running at a time. You may allow multiple instances to run in parallel with
65 /// `#[embassy_executor::task(pool_size = 4)]`, at the cost of higher RAM usage.
66 Busy,
67}
68
69impl core::fmt::Debug for SpawnError {
70 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
71 core::fmt::Display::fmt(self, f)
72 }
73}
74
75impl core::fmt::Display for SpawnError {
76 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
77 match self {
78 SpawnError::Busy => write!(
79 f,
80 "Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task."
81 ),
82 }
83 }
84}
85
86#[cfg(feature = "defmt")]
87impl defmt::Format for SpawnError {
88 fn format(&self, f: defmt::Formatter) {
89 match self {
90 SpawnError::Busy => defmt::write!(
91 f,
92 "Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task."
93 ),
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>) {
163 let task = token.raw_task;
164 mem::forget(token);
165 unsafe { self.executor.spawn(task) }
166 }
167
168 /// Convert this Spawner to a SendSpawner. This allows you to send the
169 /// spawner to other threads, but the spawner loses the ability to spawn
170 /// non-Send tasks.
171 pub fn make_send(&self) -> SendSpawner {
172 SendSpawner::new(&self.executor.inner)
173 }
174
175 /// Return the unique ID of this Spawner's Executor.
176 pub fn executor_id(&self) -> usize {
177 self.executor.id()
178 }
179}
180
181/// Handle to spawn tasks into an executor from any thread.
182///
183/// This Spawner can be used from any thread (it is Send), but it can
184/// only spawn Send tasks. The reason for this is spawning is effectively
185/// "sending" the tasks to the executor thread.
186///
187/// If you want to spawn non-Send tasks, use [Spawner].
188#[derive(Copy, Clone)]
189pub struct SendSpawner {
190 executor: &'static raw::SyncExecutor,
191}
192
193impl SendSpawner {
194 pub(crate) fn new(executor: &'static raw::SyncExecutor) -> Self {
195 Self { executor }
196 }
197
198 /// Get a Spawner for the current executor.
199 ///
200 /// This function is `async` just to get access to the current async
201 /// context. It returns instantly, it does not block/yield.
202 ///
203 /// # Panics
204 ///
205 /// Panics if the current executor is not an Embassy executor.
206 pub fn for_current_executor() -> impl Future<Output = Self> {
207 poll_fn(|cx| {
208 let task = raw::task_from_waker(cx.waker());
209 let executor = unsafe {
210 task.header()
211 .executor
212 .load(Ordering::Relaxed)
213 .as_ref()
214 .unwrap_unchecked()
215 };
216 Poll::Ready(Self::new(executor))
217 })
218 }
219
220 /// Spawn a task into an executor.
221 ///
222 /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`).
223 pub fn spawn<S: Send>(&self, token: SpawnToken<S>) {
224 let header = token.raw_task;
225 mem::forget(token);
226 unsafe { self.executor.spawn(header) }
227 }
228}