aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/src/spawner.rs
diff options
context:
space:
mode:
authorjrmoulton <[email protected]>2025-06-10 15:47:54 -0600
committerjrmoulton <[email protected]>2025-06-10 15:48:36 -0600
commitcfad9798ff99d4de0571a512d156b5fe1ef1d427 (patch)
treefc3bf670f82d139de19466cddad1e909db7f3d2e /embassy-executor/src/spawner.rs
parentfc342915e6155dec7bafa3e135da7f37a9a07f5c (diff)
parent6186d111a5c150946ee5b7e9e68d987a38c1a463 (diff)
merge new embassy changes
Diffstat (limited to 'embassy-executor/src/spawner.rs')
-rw-r--r--embassy-executor/src/spawner.rs120
1 files changed, 109 insertions, 11 deletions
diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs
index 271606244..522d97db3 100644
--- a/embassy-executor/src/spawner.rs
+++ b/embassy-executor/src/spawner.rs
@@ -1,9 +1,12 @@
1use core::future::poll_fn; 1use core::future::{poll_fn, Future};
2use core::marker::PhantomData; 2use core::marker::PhantomData;
3use core::mem; 3use core::mem;
4use core::sync::atomic::Ordering;
4use core::task::Poll; 5use core::task::Poll;
5 6
6use super::raw; 7use super::raw;
8#[cfg(feature = "trace")]
9use crate::raw::trace::TaskRefTrace;
7 10
8/// Token to spawn a newly-created task in an executor. 11/// Token to spawn a newly-created task in an executor.
9/// 12///
@@ -21,7 +24,7 @@ use super::raw;
21/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it. 24/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it.
22#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"] 25#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"]
23pub struct SpawnToken<S> { 26pub struct SpawnToken<S> {
24 raw_task: Option<raw::TaskRef>, 27 pub(crate) raw_task: Option<raw::TaskRef>,
25 phantom: PhantomData<*mut S>, 28 phantom: PhantomData<*mut S>,
26} 29}
27 30
@@ -33,6 +36,15 @@ impl<S> SpawnToken<S> {
33 } 36 }
34 } 37 }
35 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
36 /// Return a SpawnToken that represents a failed spawn. 48 /// Return a SpawnToken that represents a failed spawn.
37 pub fn new_failed() -> Self { 49 pub fn new_failed() -> Self {
38 Self { 50 Self {
@@ -50,8 +62,7 @@ impl<S> Drop for SpawnToken<S> {
50} 62}
51 63
52/// Error returned when spawning a task. 64/// Error returned when spawning a task.
53#[derive(Copy, Clone, Debug)] 65#[derive(Copy, Clone)]
54#[cfg_attr(feature = "defmt", derive(defmt::Format))]
55pub enum SpawnError { 66pub enum SpawnError {
56 /// Too many instances of this task are already running. 67 /// Too many instances of this task are already running.
57 /// 68 ///
@@ -61,6 +72,31 @@ pub enum SpawnError {
61 Busy, 72 Busy,
62} 73}
63 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
64/// Handle to spawn tasks into an executor. 100/// Handle to spawn tasks into an executor.
65/// 101///
66/// This Spawner can spawn any task (Send and non-Send ones), but it can 102/// This Spawner can spawn any task (Send and non-Send ones), but it can
@@ -69,7 +105,7 @@ pub enum SpawnError {
69/// If you want to spawn tasks from another thread, use [SendSpawner]. 105/// If you want to spawn tasks from another thread, use [SendSpawner].
70#[derive(Copy, Clone)] 106#[derive(Copy, Clone)]
71pub struct Spawner { 107pub struct Spawner {
72 executor: &'static raw::Executor, 108 pub(crate) executor: &'static raw::Executor,
73 not_send: PhantomData<*mut ()>, 109 not_send: PhantomData<*mut ()>,
74} 110}
75 111
@@ -89,14 +125,19 @@ impl Spawner {
89 /// # Panics 125 /// # Panics
90 /// 126 ///
91 /// Panics if the current executor is not an Embassy executor. 127 /// Panics if the current executor is not an Embassy executor.
92 pub async fn for_current_executor() -> Self { 128 pub fn for_current_executor() -> impl Future<Output = Self> {
93 poll_fn(|cx| { 129 poll_fn(|cx| {
94 let task = raw::task_from_waker(cx.waker()); 130 let task = raw::task_from_waker(cx.waker());
95 let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; 131 let executor = unsafe {
132 task.header()
133 .executor
134 .load(Ordering::Relaxed)
135 .as_ref()
136 .unwrap_unchecked()
137 };
96 let executor = unsafe { raw::Executor::wrap(executor) }; 138 let executor = unsafe { raw::Executor::wrap(executor) };
97 Poll::Ready(Self::new(executor)) 139 Poll::Ready(Self::new(executor))
98 }) 140 })
99 .await
100 } 141 }
101 142
102 /// Spawn a task into an executor. 143 /// Spawn a task into an executor.
@@ -134,6 +175,58 @@ impl Spawner {
134 pub fn make_send(&self) -> SendSpawner { 175 pub fn make_send(&self) -> SendSpawner {
135 SendSpawner::new(&self.executor.inner) 176 SendSpawner::new(&self.executor.inner)
136 } 177 }
178
179 /// Return the unique ID of this Spawner's Executor.
180 pub fn executor_id(&self) -> usize {
181 self.executor.id()
182 }
183}
184
185/// Extension trait adding tracing capabilities to the Spawner
186///
187/// This trait provides an additional method to spawn tasks with an associated name,
188/// which can be useful for debugging and tracing purposes.
189pub trait SpawnerTraceExt {
190 /// Spawns a new task with a specified name.
191 ///
192 /// # Arguments
193 /// * `name` - Static string name to associate with the task
194 /// * `token` - Token representing the task to spawn
195 ///
196 /// # Returns
197 /// Result indicating whether the spawn was successful
198 fn spawn_named<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError>;
199}
200
201/// Implementation of the SpawnerTraceExt trait for Spawner when trace is enabled
202#[cfg(feature = "trace")]
203impl SpawnerTraceExt for Spawner {
204 fn spawn_named<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> {
205 let task = token.raw_task;
206 core::mem::forget(token);
207
208 match task {
209 Some(task) => {
210 // Set the name and ID when trace is enabled
211 task.set_name(Some(name));
212 let task_id = task.as_ptr() as u32;
213 task.set_id(task_id);
214
215 unsafe { self.executor.spawn(task) };
216 Ok(())
217 }
218 None => Err(SpawnError::Busy),
219 }
220 }
221}
222
223/// Implementation of the SpawnerTraceExt trait for Spawner when trace is disabled
224#[cfg(not(feature = "trace"))]
225impl SpawnerTraceExt for Spawner {
226 fn spawn_named<S>(&self, _name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError> {
227 // When trace is disabled, just forward to regular spawn and ignore the name
228 self.spawn(token)
229 }
137} 230}
138 231
139/// Handle to spawn tasks into an executor from any thread. 232/// Handle to spawn tasks into an executor from any thread.
@@ -161,13 +254,18 @@ impl SendSpawner {
161 /// # Panics 254 /// # Panics
162 /// 255 ///
163 /// Panics if the current executor is not an Embassy executor. 256 /// Panics if the current executor is not an Embassy executor.
164 pub async fn for_current_executor() -> Self { 257 pub fn for_current_executor() -> impl Future<Output = Self> {
165 poll_fn(|cx| { 258 poll_fn(|cx| {
166 let task = raw::task_from_waker(cx.waker()); 259 let task = raw::task_from_waker(cx.waker());
167 let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; 260 let executor = unsafe {
261 task.header()
262 .executor
263 .load(Ordering::Relaxed)
264 .as_ref()
265 .unwrap_unchecked()
266 };
168 Poll::Ready(Self::new(executor)) 267 Poll::Ready(Self::new(executor))
169 }) 268 })
170 .await
171 } 269 }
172 270
173 /// Spawn a task into an executor. 271 /// Spawn a task into an executor.