aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/src
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-executor/src')
-rw-r--r--embassy-executor/src/lib.rs3
-rw-r--r--embassy-executor/src/metadata.rs55
-rw-r--r--embassy-executor/src/raw/mod.rs62
-rw-r--r--embassy-executor/src/raw/trace.rs77
-rw-r--r--embassy-executor/src/spawner.rs117
5 files changed, 126 insertions, 188 deletions
diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs
index 0747db032..e47b8eb9f 100644
--- a/embassy-executor/src/lib.rs
+++ b/embassy-executor/src/lib.rs
@@ -54,6 +54,9 @@ pub mod raw;
54mod spawner; 54mod spawner;
55pub use spawner::*; 55pub use spawner::*;
56 56
57mod metadata;
58pub use metadata::*;
59
57/// Implementation details for embassy macros. 60/// Implementation details for embassy macros.
58/// Do not use. Used for macros and HALs only. Not covered by semver guarantees. 61/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
59#[doc(hidden)] 62#[doc(hidden)]
diff --git a/embassy-executor/src/metadata.rs b/embassy-executor/src/metadata.rs
new file mode 100644
index 000000000..f92c9b37c
--- /dev/null
+++ b/embassy-executor/src/metadata.rs
@@ -0,0 +1,55 @@
1#[cfg(feature = "metadata-name")]
2use core::cell::Cell;
3use core::future::{poll_fn, Future};
4use core::task::Poll;
5
6#[cfg(feature = "metadata-name")]
7use critical_section::Mutex;
8
9use crate::raw;
10
11/// Metadata associated with a task.
12pub struct Metadata {
13 #[cfg(feature = "metadata-name")]
14 name: Mutex<Cell<Option<&'static str>>>,
15}
16
17impl Metadata {
18 pub(crate) const fn new() -> Self {
19 Self {
20 #[cfg(feature = "metadata-name")]
21 name: Mutex::new(Cell::new(None)),
22 }
23 }
24
25 pub(crate) fn reset(&self) {
26 #[cfg(feature = "metadata-name")]
27 critical_section::with(|cs| self.name.borrow(cs).set(None));
28 }
29
30 /// Get the metadata for the current task.
31 ///
32 /// You can use this to read or modify the current task's metadata.
33 ///
34 /// This function is `async` just to get access to the current async
35 /// context. It returns instantly, it does not block/yield.
36 pub fn for_current_task() -> impl Future<Output = &'static Self> {
37 poll_fn(|cx| Poll::Ready(raw::task_from_waker(cx.waker()).metadata()))
38 }
39
40 /// Get this task's name
41 ///
42 /// NOTE: this takes a critical section.
43 #[cfg(feature = "metadata-name")]
44 pub fn name(&self) -> Option<&'static str> {
45 critical_section::with(|cs| self.name.borrow(cs).get())
46 }
47
48 /// Set this task's name
49 ///
50 /// NOTE: this takes a critical section.
51 #[cfg(feature = "metadata-name")]
52 pub fn set_name(&self, name: &'static str) {
53 critical_section::with(|cs| self.name.borrow(cs).set(Some(name)))
54 }
55}
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs
index 4b17d4982..bdaa32951 100644
--- a/embassy-executor/src/raw/mod.rs
+++ b/embassy-executor/src/raw/mod.rs
@@ -16,7 +16,7 @@ mod run_queue;
16#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")] 16#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")]
17mod state; 17mod state;
18 18
19#[cfg(feature = "trace")] 19#[cfg(feature = "_any_trace")]
20pub mod trace; 20pub mod trace;
21pub(crate) mod util; 21pub(crate) mod util;
22#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] 22#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")]
@@ -41,6 +41,7 @@ use self::state::State;
41use self::util::{SyncUnsafeCell, UninitCell}; 41use self::util::{SyncUnsafeCell, UninitCell};
42pub use self::waker::task_from_waker; 42pub use self::waker::task_from_waker;
43use super::SpawnToken; 43use super::SpawnToken;
44use crate::{Metadata, SpawnError};
44 45
45#[no_mangle] 46#[no_mangle]
46extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem { 47extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem {
@@ -94,11 +95,10 @@ pub(crate) struct TaskHeader {
94 95
95 /// Integrated timer queue storage. This field should not be accessed outside of the timer queue. 96 /// Integrated timer queue storage. This field should not be accessed outside of the timer queue.
96 pub(crate) timer_queue_item: TimerQueueItem, 97 pub(crate) timer_queue_item: TimerQueueItem,
97 #[cfg(feature = "trace")] 98
98 pub(crate) name: Option<&'static str>, 99 pub(crate) metadata: Metadata,
99 #[cfg(feature = "trace")] 100
100 pub(crate) id: u32, 101 #[cfg(feature = "rtos-trace")]
101 #[cfg(feature = "trace")]
102 all_tasks_next: AtomicPtr<TaskHeader>, 102 all_tasks_next: AtomicPtr<TaskHeader>,
103} 103}
104 104
@@ -129,6 +129,10 @@ impl TaskRef {
129 unsafe { self.ptr.as_ref() } 129 unsafe { self.ptr.as_ref() }
130 } 130 }
131 131
132 pub(crate) fn metadata(self) -> &'static Metadata {
133 unsafe { &self.ptr.as_ref().metadata }
134 }
135
132 /// Returns a reference to the executor that the task is currently running on. 136 /// Returns a reference to the executor that the task is currently running on.
133 pub unsafe fn executor(self) -> Option<&'static Executor> { 137 pub unsafe fn executor(self) -> Option<&'static Executor> {
134 let executor = self.header().executor.load(Ordering::Relaxed); 138 let executor = self.header().executor.load(Ordering::Relaxed);
@@ -148,6 +152,12 @@ impl TaskRef {
148 pub(crate) fn as_ptr(self) -> *const TaskHeader { 152 pub(crate) fn as_ptr(self) -> *const TaskHeader {
149 self.ptr.as_ptr() 153 self.ptr.as_ptr()
150 } 154 }
155
156 /// Returns the task ID.
157 /// This can be used in combination with rtos-trace to match task names with IDs
158 pub fn id(&self) -> u32 {
159 self.as_ptr() as u32
160 }
151} 161}
152 162
153/// Raw storage in which a task can be spawned. 163/// Raw storage in which a task can be spawned.
@@ -189,11 +199,8 @@ impl<F: Future + 'static> TaskStorage<F> {
189 poll_fn: SyncUnsafeCell::new(None), 199 poll_fn: SyncUnsafeCell::new(None),
190 200
191 timer_queue_item: TimerQueueItem::new(), 201 timer_queue_item: TimerQueueItem::new(),
192 #[cfg(feature = "trace")] 202 metadata: Metadata::new(),
193 name: None, 203 #[cfg(feature = "rtos-trace")]
194 #[cfg(feature = "trace")]
195 id: 0,
196 #[cfg(feature = "trace")]
197 all_tasks_next: AtomicPtr::new(core::ptr::null_mut()), 204 all_tasks_next: AtomicPtr::new(core::ptr::null_mut()),
198 }, 205 },
199 future: UninitCell::uninit(), 206 future: UninitCell::uninit(),
@@ -213,11 +220,11 @@ impl<F: Future + 'static> TaskStorage<F> {
213 /// 220 ///
214 /// Once the task has finished running, you may spawn it again. It is allowed to spawn it 221 /// Once the task has finished running, you may spawn it again. It is allowed to spawn it
215 /// on a different executor. 222 /// on a different executor.
216 pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> { 223 pub fn spawn(&'static self, future: impl FnOnce() -> F) -> Result<SpawnToken<impl Sized>, SpawnError> {
217 let task = AvailableTask::claim(self); 224 let task = AvailableTask::claim(self);
218 match task { 225 match task {
219 Some(task) => task.initialize(future), 226 Some(task) => Ok(task.initialize(future)),
220 None => SpawnToken::new_failed(), 227 None => Err(SpawnError::Busy),
221 } 228 }
222 } 229 }
223 230
@@ -229,7 +236,7 @@ impl<F: Future + 'static> TaskStorage<F> {
229 let mut cx = Context::from_waker(&waker); 236 let mut cx = Context::from_waker(&waker);
230 match future.poll(&mut cx) { 237 match future.poll(&mut cx) {
231 Poll::Ready(_) => { 238 Poll::Ready(_) => {
232 #[cfg(feature = "trace")] 239 #[cfg(feature = "_any_trace")]
233 let exec_ptr: *const SyncExecutor = this.raw.executor.load(Ordering::Relaxed); 240 let exec_ptr: *const SyncExecutor = this.raw.executor.load(Ordering::Relaxed);
234 241
235 // As the future has finished and this function will not be called 242 // As the future has finished and this function will not be called
@@ -244,7 +251,7 @@ impl<F: Future + 'static> TaskStorage<F> {
244 // after we're done with it. 251 // after we're done with it.
245 this.raw.state.despawn(); 252 this.raw.state.despawn();
246 253
247 #[cfg(feature = "trace")] 254 #[cfg(feature = "_any_trace")]
248 trace::task_end(exec_ptr, &p); 255 trace::task_end(exec_ptr, &p);
249 } 256 }
250 Poll::Pending => {} 257 Poll::Pending => {}
@@ -279,6 +286,7 @@ impl<F: Future + 'static> AvailableTask<F> {
279 286
280 fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> { 287 fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
281 unsafe { 288 unsafe {
289 self.task.raw.metadata.reset();
282 self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll)); 290 self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
283 self.task.future.write_in_place(future); 291 self.task.future.write_in_place(future);
284 292
@@ -345,10 +353,10 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
345 } 353 }
346 } 354 }
347 355
348 fn spawn_impl<T>(&'static self, future: impl FnOnce() -> F) -> SpawnToken<T> { 356 fn spawn_impl<T>(&'static self, future: impl FnOnce() -> F) -> Result<SpawnToken<T>, SpawnError> {
349 match self.pool.iter().find_map(AvailableTask::claim) { 357 match self.pool.iter().find_map(AvailableTask::claim) {
350 Some(task) => task.initialize_impl::<T>(future), 358 Some(task) => Ok(task.initialize_impl::<T>(future)),
351 None => SpawnToken::new_failed(), 359 None => Err(SpawnError::Busy),
352 } 360 }
353 } 361 }
354 362
@@ -359,7 +367,7 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
359 /// This will loop over the pool and spawn the task in the first storage that 367 /// This will loop over the pool and spawn the task in the first storage that
360 /// is currently free. If none is free, a "poisoned" SpawnToken is returned, 368 /// is currently free. If none is free, a "poisoned" SpawnToken is returned,
361 /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. 369 /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.
362 pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> { 370 pub fn spawn(&'static self, future: impl FnOnce() -> F) -> Result<SpawnToken<impl Sized>, SpawnError> {
363 self.spawn_impl::<F>(future) 371 self.spawn_impl::<F>(future)
364 } 372 }
365 373
@@ -372,7 +380,7 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
372 /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` 380 /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`
373 /// is an `async fn`, NOT a hand-written `Future`. 381 /// is an `async fn`, NOT a hand-written `Future`.
374 #[doc(hidden)] 382 #[doc(hidden)]
375 pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> SpawnToken<impl Sized> 383 pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> Result<SpawnToken<impl Sized>, SpawnError>
376 where 384 where
377 FutFn: FnOnce() -> F, 385 FutFn: FnOnce() -> F,
378 { 386 {
@@ -417,7 +425,7 @@ impl SyncExecutor {
417 /// - `task` must NOT be already enqueued (in this executor or another one). 425 /// - `task` must NOT be already enqueued (in this executor or another one).
418 #[inline(always)] 426 #[inline(always)]
419 unsafe fn enqueue(&self, task: TaskRef, l: state::Token) { 427 unsafe fn enqueue(&self, task: TaskRef, l: state::Token) {
420 #[cfg(feature = "trace")] 428 #[cfg(feature = "_any_trace")]
421 trace::task_ready_begin(self, &task); 429 trace::task_ready_begin(self, &task);
422 430
423 if self.run_queue.enqueue(task, l) { 431 if self.run_queue.enqueue(task, l) {
@@ -430,7 +438,7 @@ impl SyncExecutor {
430 .executor 438 .executor
431 .store((self as *const Self).cast_mut(), Ordering::Relaxed); 439 .store((self as *const Self).cast_mut(), Ordering::Relaxed);
432 440
433 #[cfg(feature = "trace")] 441 #[cfg(feature = "_any_trace")]
434 trace::task_new(self, &task); 442 trace::task_new(self, &task);
435 443
436 state::locked(|l| { 444 state::locked(|l| {
@@ -442,23 +450,23 @@ impl SyncExecutor {
442 /// 450 ///
443 /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. 451 /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created.
444 pub(crate) unsafe fn poll(&'static self) { 452 pub(crate) unsafe fn poll(&'static self) {
445 #[cfg(feature = "trace")] 453 #[cfg(feature = "_any_trace")]
446 trace::poll_start(self); 454 trace::poll_start(self);
447 455
448 self.run_queue.dequeue_all(|p| { 456 self.run_queue.dequeue_all(|p| {
449 let task = p.header(); 457 let task = p.header();
450 458
451 #[cfg(feature = "trace")] 459 #[cfg(feature = "_any_trace")]
452 trace::task_exec_begin(self, &p); 460 trace::task_exec_begin(self, &p);
453 461
454 // Run the task 462 // Run the task
455 task.poll_fn.get().unwrap_unchecked()(p); 463 task.poll_fn.get().unwrap_unchecked()(p);
456 464
457 #[cfg(feature = "trace")] 465 #[cfg(feature = "_any_trace")]
458 trace::task_exec_end(self, &p); 466 trace::task_exec_end(self, &p);
459 }); 467 });
460 468
461 #[cfg(feature = "trace")] 469 #[cfg(feature = "_any_trace")]
462 trace::executor_idle(self) 470 trace::executor_idle(self)
463 } 471 }
464} 472}
diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs
index f484abf58..b3086948c 100644
--- a/embassy-executor/src/raw/trace.rs
+++ b/embassy-executor/src/raw/trace.rs
@@ -95,17 +95,20 @@ use crate::spawner::{SpawnError, SpawnToken, Spawner};
95/// This static provides access to the global task tracker which maintains 95/// This static provides access to the global task tracker which maintains
96/// a list of all tasks in the system. It's automatically updated by the 96/// a list of all tasks in the system. It's automatically updated by the
97/// task lifecycle hooks in the trace module. 97/// task lifecycle hooks in the trace module.
98pub static TASK_TRACKER: TaskTracker = TaskTracker::new(); 98#[cfg(feature = "rtos-trace")]
99pub(crate) static TASK_TRACKER: TaskTracker = TaskTracker::new();
99 100
100/// A thread-safe tracker for all tasks in the system 101/// A thread-safe tracker for all tasks in the system
101/// 102///
102/// This struct uses an intrusive linked list approach to track all tasks 103/// This struct uses an intrusive linked list approach to track all tasks
103/// without additional memory allocations. It maintains a global list of 104/// without additional memory allocations. It maintains a global list of
104/// tasks that can be traversed to find all currently existing tasks. 105/// tasks that can be traversed to find all currently existing tasks.
105pub struct TaskTracker { 106#[cfg(feature = "rtos-trace")]
107pub(crate) struct TaskTracker {
106 head: AtomicPtr<TaskHeader>, 108 head: AtomicPtr<TaskHeader>,
107} 109}
108 110
111#[cfg(feature = "rtos-trace")]
109impl TaskTracker { 112impl TaskTracker {
110 /// Creates a new empty task tracker 113 /// Creates a new empty task tracker
111 /// 114 ///
@@ -125,7 +128,7 @@ impl TaskTracker {
125 /// # Arguments 128 /// # Arguments
126 /// * `task` - The task reference to add to the tracker 129 /// * `task` - The task reference to add to the tracker
127 pub fn add(&self, task: TaskRef) { 130 pub fn add(&self, task: TaskRef) {
128 let task_ptr = task.as_ptr() as *mut TaskHeader; 131 let task_ptr = task.as_ptr();
129 132
130 loop { 133 loop {
131 let current_head = self.head.load(Ordering::Acquire); 134 let current_head = self.head.load(Ordering::Acquire);
@@ -135,7 +138,7 @@ impl TaskTracker {
135 138
136 if self 139 if self
137 .head 140 .head
138 .compare_exchange(current_head, task_ptr, Ordering::Release, Ordering::Relaxed) 141 .compare_exchange(current_head, task_ptr.cast_mut(), Ordering::Release, Ordering::Relaxed)
139 .is_ok() 142 .is_ok()
140 { 143 {
141 break; 144 break;
@@ -165,50 +168,7 @@ impl TaskTracker {
165 } 168 }
166} 169}
167 170
168/// Extension trait for `TaskRef` that provides tracing functionality. 171#[cfg(feature = "trace")]
169///
170/// This trait is only available when the `trace` feature is enabled.
171/// It extends `TaskRef` with methods for accessing and modifying task identifiers
172/// and names, which are useful for debugging, logging, and performance analysis.
173pub trait TaskRefTrace {
174 /// Get the name for a task
175 fn name(&self) -> Option<&'static str>;
176
177 /// Set the name for a task
178 fn set_name(&self, name: Option<&'static str>);
179
180 /// Get the ID for a task
181 fn id(&self) -> u32;
182
183 /// Set the ID for a task
184 fn set_id(&self, id: u32);
185}
186
187impl TaskRefTrace for TaskRef {
188 fn name(&self) -> Option<&'static str> {
189 self.header().name
190 }
191
192 fn set_name(&self, name: Option<&'static str>) {
193 unsafe {
194 let header_ptr = self.ptr.as_ptr() as *mut TaskHeader;
195 (*header_ptr).name = name;
196 }
197 }
198
199 fn id(&self) -> u32 {
200 self.header().id
201 }
202
203 fn set_id(&self, id: u32) {
204 unsafe {
205 let header_ptr = self.ptr.as_ptr() as *mut TaskHeader;
206 (*header_ptr).id = id;
207 }
208 }
209}
210
211#[cfg(not(feature = "rtos-trace"))]
212extern "Rust" { 172extern "Rust" {
213 /// This callback is called when the executor begins polling. This will always 173 /// This callback is called when the executor begins polling. This will always
214 /// be paired with a later call to `_embassy_trace_executor_idle`. 174 /// be paired with a later call to `_embassy_trace_executor_idle`.
@@ -270,7 +230,7 @@ extern "Rust" {
270 230
271#[inline] 231#[inline]
272pub(crate) fn poll_start(executor: &SyncExecutor) { 232pub(crate) fn poll_start(executor: &SyncExecutor) {
273 #[cfg(not(feature = "rtos-trace"))] 233 #[cfg(feature = "trace")]
274 unsafe { 234 unsafe {
275 _embassy_trace_poll_start(executor as *const _ as u32) 235 _embassy_trace_poll_start(executor as *const _ as u32)
276 } 236 }
@@ -278,7 +238,7 @@ pub(crate) fn poll_start(executor: &SyncExecutor) {
278 238
279#[inline] 239#[inline]
280pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) { 240pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) {
281 #[cfg(not(feature = "rtos-trace"))] 241 #[cfg(feature = "trace")]
282 unsafe { 242 unsafe {
283 _embassy_trace_task_new(executor as *const _ as u32, task.as_ptr() as u32) 243 _embassy_trace_task_new(executor as *const _ as u32, task.as_ptr() as u32)
284 } 244 }
@@ -286,7 +246,7 @@ pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) {
286 #[cfg(feature = "rtos-trace")] 246 #[cfg(feature = "rtos-trace")]
287 { 247 {
288 rtos_trace::trace::task_new(task.as_ptr() as u32); 248 rtos_trace::trace::task_new(task.as_ptr() as u32);
289 let name = task.name().unwrap_or("unnamed task\0"); 249 let name = task.metadata().name().unwrap_or("unnamed task\0");
290 let info = rtos_trace::TaskInfo { 250 let info = rtos_trace::TaskInfo {
291 name, 251 name,
292 priority: 0, 252 priority: 0,
@@ -302,7 +262,7 @@ pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) {
302 262
303#[inline] 263#[inline]
304pub(crate) fn task_end(executor: *const SyncExecutor, task: &TaskRef) { 264pub(crate) fn task_end(executor: *const SyncExecutor, task: &TaskRef) {
305 #[cfg(not(feature = "rtos-trace"))] 265 #[cfg(feature = "trace")]
306 unsafe { 266 unsafe {
307 _embassy_trace_task_end(executor as u32, task.as_ptr() as u32) 267 _embassy_trace_task_end(executor as u32, task.as_ptr() as u32)
308 } 268 }
@@ -310,7 +270,7 @@ pub(crate) fn task_end(executor: *const SyncExecutor, task: &TaskRef) {
310 270
311#[inline] 271#[inline]
312pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) { 272pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) {
313 #[cfg(not(feature = "rtos-trace"))] 273 #[cfg(feature = "trace")]
314 unsafe { 274 unsafe {
315 _embassy_trace_task_ready_begin(executor as *const _ as u32, task.as_ptr() as u32) 275 _embassy_trace_task_ready_begin(executor as *const _ as u32, task.as_ptr() as u32)
316 } 276 }
@@ -320,7 +280,7 @@ pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) {
320 280
321#[inline] 281#[inline]
322pub(crate) fn task_exec_begin(executor: &SyncExecutor, task: &TaskRef) { 282pub(crate) fn task_exec_begin(executor: &SyncExecutor, task: &TaskRef) {
323 #[cfg(not(feature = "rtos-trace"))] 283 #[cfg(feature = "trace")]
324 unsafe { 284 unsafe {
325 _embassy_trace_task_exec_begin(executor as *const _ as u32, task.as_ptr() as u32) 285 _embassy_trace_task_exec_begin(executor as *const _ as u32, task.as_ptr() as u32)
326 } 286 }
@@ -330,7 +290,7 @@ pub(crate) fn task_exec_begin(executor: &SyncExecutor, task: &TaskRef) {
330 290
331#[inline] 291#[inline]
332pub(crate) fn task_exec_end(executor: &SyncExecutor, task: &TaskRef) { 292pub(crate) fn task_exec_end(executor: &SyncExecutor, task: &TaskRef) {
333 #[cfg(not(feature = "rtos-trace"))] 293 #[cfg(feature = "trace")]
334 unsafe { 294 unsafe {
335 _embassy_trace_task_exec_end(executor as *const _ as u32, task.as_ptr() as u32) 295 _embassy_trace_task_exec_end(executor as *const _ as u32, task.as_ptr() as u32)
336 } 296 }
@@ -340,7 +300,7 @@ pub(crate) fn task_exec_end(executor: &SyncExecutor, task: &TaskRef) {
340 300
341#[inline] 301#[inline]
342pub(crate) fn executor_idle(executor: &SyncExecutor) { 302pub(crate) fn executor_idle(executor: &SyncExecutor) {
343 #[cfg(not(feature = "rtos-trace"))] 303 #[cfg(feature = "trace")]
344 unsafe { 304 unsafe {
345 _embassy_trace_executor_idle(executor as *const _ as u32) 305 _embassy_trace_executor_idle(executor as *const _ as u32)
346 } 306 }
@@ -356,6 +316,7 @@ pub(crate) fn executor_idle(executor: &SyncExecutor) {
356/// 316///
357/// # Returns 317/// # Returns
358/// An iterator that yields `TaskRef` items for each task 318/// An iterator that yields `TaskRef` items for each task
319#[cfg(feature = "rtos-trace")]
359fn get_all_active_tasks() -> impl Iterator<Item = TaskRef> + 'static { 320fn get_all_active_tasks() -> impl Iterator<Item = TaskRef> + 'static {
360 struct TaskIterator<'a> { 321 struct TaskIterator<'a> {
361 tracker: &'a TaskTracker, 322 tracker: &'a TaskTracker,
@@ -384,6 +345,7 @@ fn get_all_active_tasks() -> impl Iterator<Item = TaskRef> + 'static {
384} 345}
385 346
386/// Perform an action on each active task 347/// Perform an action on each active task
348#[cfg(feature = "rtos-trace")]
387fn with_all_active_tasks<F>(f: F) 349fn with_all_active_tasks<F>(f: F)
388where 350where
389 F: FnMut(TaskRef), 351 F: FnMut(TaskRef),
@@ -395,9 +357,8 @@ where
395impl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor { 357impl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor {
396 fn task_list() { 358 fn task_list() {
397 with_all_active_tasks(|task| { 359 with_all_active_tasks(|task| {
398 let name = task.name().unwrap_or("unnamed task\0");
399 let info = rtos_trace::TaskInfo { 360 let info = rtos_trace::TaskInfo {
400 name, 361 name: task.metadata().name().unwrap_or("unnamed task\0"),
401 priority: 0, 362 priority: 0,
402 stack_base: 0, 363 stack_base: 0,
403 stack_size: 0, 364 stack_size: 0,
diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs
index 2909d19a0..83d896b76 100644
--- a/embassy-executor/src/spawner.rs
+++ b/embassy-executor/src/spawner.rs
@@ -5,8 +5,7 @@ use core::sync::atomic::Ordering;
5use core::task::Poll; 5use core::task::Poll;
6 6
7use super::raw; 7use super::raw;
8#[cfg(feature = "trace")] 8use crate::Metadata;
9use crate::raw::trace::TaskRefTrace;
10 9
11/// Token to spawn a newly-created task in an executor. 10/// Token to spawn a newly-created task in an executor.
12/// 11///
@@ -24,33 +23,28 @@ use crate::raw::trace::TaskRefTrace;
24/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it. 23/// 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()"] 24#[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> { 25pub struct SpawnToken<S> {
27 pub(crate) raw_task: Option<raw::TaskRef>, 26 pub(crate) raw_task: raw::TaskRef,
28 phantom: PhantomData<*mut S>, 27 phantom: PhantomData<*mut S>,
29} 28}
30 29
31impl<S> SpawnToken<S> { 30impl<S> SpawnToken<S> {
32 pub(crate) unsafe fn new(raw_task: raw::TaskRef) -> Self { 31 pub(crate) unsafe fn new(raw_task: raw::TaskRef) -> Self {
33 Self { 32 Self {
34 raw_task: Some(raw_task), 33 raw_task,
35 phantom: PhantomData, 34 phantom: PhantomData,
36 } 35 }
37 } 36 }
38 37
39 /// Returns the task id if available, otherwise 0 38 /// Returns the task ID.
40 /// This can be used in combination with rtos-trace to match task names with id's 39 /// This can be used in combination with rtos-trace to match task names with IDs
41 pub fn id(&self) -> u32 { 40 pub fn id(&self) -> u32 {
42 match self.raw_task { 41 self.raw_task.id()
43 None => 0,
44 Some(t) => t.as_ptr() as u32,
45 }
46 } 42 }
47 43
48 /// Return a SpawnToken that represents a failed spawn. 44 /// Get the metadata for this task. You can use this to set metadata fields
49 pub fn new_failed() -> Self { 45 /// prior to spawning it.
50 Self { 46 pub fn metadata(&self) -> &Metadata {
51 raw_task: None, 47 self.raw_task.metadata()
52 phantom: PhantomData,
53 }
54 } 48 }
55} 49}
56 50
@@ -159,30 +153,10 @@ impl Spawner {
159 /// Spawn a task into an executor. 153 /// Spawn a task into an executor.
160 /// 154 ///
161 /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`). 155 /// 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> { 156 pub fn spawn<S>(&self, token: SpawnToken<S>) {
163 let task = token.raw_task; 157 let task = token.raw_task;
164 mem::forget(token); 158 mem::forget(token);
165 159 unsafe { self.executor.spawn(task) }
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 } 160 }
187 161
188 /// Convert this Spawner to a SendSpawner. This allows you to send the 162 /// Convert this Spawner to a SendSpawner. This allows you to send the
@@ -198,53 +172,6 @@ impl Spawner {
198 } 172 }
199} 173}
200 174
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. 175/// Handle to spawn tasks into an executor from any thread.
249/// 176///
250/// This Spawner can be used from any thread (it is Send), but it can 177/// This Spawner can be used from any thread (it is Send), but it can
@@ -287,25 +214,9 @@ impl SendSpawner {
287 /// Spawn a task into an executor. 214 /// Spawn a task into an executor.
288 /// 215 ///
289 /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`). 216 /// 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> { 217 pub fn spawn<S: Send>(&self, token: SpawnToken<S>) {
291 let header = token.raw_task; 218 let header = token.raw_task;
292 mem::forget(token); 219 mem::forget(token);
293 220 unsafe { self.executor.spawn(header) }
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 } 221 }
311} 222}