From 658a52fb99e47d3d2f08ebf66335774930ad35ac Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 8 Jul 2025 23:29:31 +0200 Subject: executor: do not store task IDs in RAM, we can get it from the pointer every time. --- embassy-executor/src/metadata.rs | 1 + embassy-executor/src/raw/mod.rs | 10 ++++++---- embassy-executor/src/raw/trace.rs | 17 ----------------- embassy-executor/src/spawner.rs | 10 ++++------ 4 files changed, 11 insertions(+), 27 deletions(-) create mode 100644 embassy-executor/src/metadata.rs (limited to 'embassy-executor') diff --git a/embassy-executor/src/metadata.rs b/embassy-executor/src/metadata.rs new file mode 100644 index 000000000..957417f6b --- /dev/null +++ b/embassy-executor/src/metadata.rs @@ -0,0 +1 @@ +pub struct Metadata {} diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 4b17d4982..bcd4ee432 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -97,8 +97,6 @@ pub(crate) struct TaskHeader { #[cfg(feature = "trace")] pub(crate) name: Option<&'static str>, #[cfg(feature = "trace")] - pub(crate) id: u32, - #[cfg(feature = "trace")] all_tasks_next: AtomicPtr, } @@ -148,6 +146,12 @@ impl TaskRef { pub(crate) fn as_ptr(self) -> *const TaskHeader { self.ptr.as_ptr() } + + /// Returns the task ID. + /// This can be used in combination with rtos-trace to match task names with IDs + pub fn id(&self) -> u32 { + self.as_ptr() as u32 + } } /// Raw storage in which a task can be spawned. @@ -192,8 +196,6 @@ impl TaskStorage { #[cfg(feature = "trace")] name: None, #[cfg(feature = "trace")] - id: 0, - #[cfg(feature = "trace")] all_tasks_next: AtomicPtr::new(core::ptr::null_mut()), }, future: UninitCell::uninit(), diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs index f484abf58..e769d63da 100644 --- a/embassy-executor/src/raw/trace.rs +++ b/embassy-executor/src/raw/trace.rs @@ -176,12 +176,6 @@ pub trait TaskRefTrace { /// Set the name for a task fn set_name(&self, name: Option<&'static str>); - - /// Get the ID for a task - fn id(&self) -> u32; - - /// Set the ID for a task - fn set_id(&self, id: u32); } impl TaskRefTrace for TaskRef { @@ -195,17 +189,6 @@ impl TaskRefTrace for TaskRef { (*header_ptr).name = name; } } - - fn id(&self) -> u32 { - self.header().id - } - - fn set_id(&self, id: u32) { - unsafe { - let header_ptr = self.ptr.as_ptr() as *mut TaskHeader; - (*header_ptr).id = id; - } - } } #[cfg(not(feature = "rtos-trace"))] diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 2909d19a0..7550e8ea4 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -36,12 +36,12 @@ impl SpawnToken { } } - /// Returns the task id if available, otherwise 0 - /// This can be used in combination with rtos-trace to match task names with id's + /// Returns the task ID if available, otherwise 0 + /// This can be used in combination with rtos-trace to match task names with IDs pub fn id(&self) -> u32 { match self.raw_task { None => 0, - Some(t) => t.as_ptr() as u32, + Some(t) => t.id(), } } @@ -223,10 +223,8 @@ impl SpawnerTraceExt for Spawner { match task { Some(task) => { - // Set the name and ID when trace is enabled + // Set the name when trace is enabled task.set_name(Some(name)); - let task_id = task.as_ptr() as u32; - task.set_id(task_id); unsafe { self.executor.spawn(task) }; Ok(()) -- cgit From 2ba34ce2178d576f339f0b0dac70ac125f81cc5b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 8 Jul 2025 23:36:51 +0200 Subject: executor: allow trace and rtos-trace to coexist additively. Before, enabling `trace` would enable embassy-native tracing, and enabling *both* would *disable* embassy-native tracing. --- embassy-executor/Cargo.toml | 23 ++++++++++++++++++++--- embassy-executor/src/raw/mod.rs | 26 +++++++++++++------------- embassy-executor/src/raw/trace.rs | 25 +++++++++++++++---------- 3 files changed, 48 insertions(+), 26 deletions(-) (limited to 'embassy-executor') diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index d89e85cb7..a36a61885 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -115,7 +115,24 @@ arch-spin = ["_arch"] executor-thread = [] ## Enable the interrupt-mode executor (available in Cortex-M only) executor-interrupt = [] -## Enable tracing support (adds some overhead) -trace = [] +## Enable tracing hooks +trace = ["_any_trace"] ## Enable support for rtos-trace framework -rtos-trace = ["dep:rtos-trace", "trace", "dep:embassy-time-driver"] +rtos-trace = ["dep:rtos-trace", "_any_trace", "dep:embassy-time-driver"] +_any_trace = [] + +#! ### Timer Item Payload Size +#! Sets the size of the payload for timer items, allowing integrated timer implementors to store +#! additional data in the timer item. The payload field will be aligned to this value as well. +#! If these features are not defined, the timer item will contain no payload field. + +_timer-item-payload = [] # A size was picked + +## 1 bytes +timer-item-payload-size-1 = ["_timer-item-payload"] +## 2 bytes +timer-item-payload-size-2 = ["_timer-item-payload"] +## 4 bytes +timer-item-payload-size-4 = ["_timer-item-payload"] +## 8 bytes +timer-item-payload-size-8 = ["_timer-item-payload"] diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index bcd4ee432..87328df5a 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -16,7 +16,7 @@ mod run_queue; #[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")] mod state; -#[cfg(feature = "trace")] +#[cfg(feature = "_any_trace")] pub mod trace; pub(crate) mod util; #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] @@ -94,9 +94,9 @@ pub(crate) struct TaskHeader { /// Integrated timer queue storage. This field should not be accessed outside of the timer queue. pub(crate) timer_queue_item: TimerQueueItem, - #[cfg(feature = "trace")] + #[cfg(feature = "_any_trace")] pub(crate) name: Option<&'static str>, - #[cfg(feature = "trace")] + #[cfg(feature = "rtos-trace")] all_tasks_next: AtomicPtr, } @@ -193,9 +193,9 @@ impl TaskStorage { poll_fn: SyncUnsafeCell::new(None), timer_queue_item: TimerQueueItem::new(), - #[cfg(feature = "trace")] + #[cfg(feature = "_any_trace")] name: None, - #[cfg(feature = "trace")] + #[cfg(feature = "rtos-trace")] all_tasks_next: AtomicPtr::new(core::ptr::null_mut()), }, future: UninitCell::uninit(), @@ -231,7 +231,7 @@ impl TaskStorage { let mut cx = Context::from_waker(&waker); match future.poll(&mut cx) { Poll::Ready(_) => { - #[cfg(feature = "trace")] + #[cfg(feature = "_any_trace")] let exec_ptr: *const SyncExecutor = this.raw.executor.load(Ordering::Relaxed); // As the future has finished and this function will not be called @@ -246,7 +246,7 @@ impl TaskStorage { // after we're done with it. this.raw.state.despawn(); - #[cfg(feature = "trace")] + #[cfg(feature = "_any_trace")] trace::task_end(exec_ptr, &p); } Poll::Pending => {} @@ -419,7 +419,7 @@ impl SyncExecutor { /// - `task` must NOT be already enqueued (in this executor or another one). #[inline(always)] unsafe fn enqueue(&self, task: TaskRef, l: state::Token) { - #[cfg(feature = "trace")] + #[cfg(feature = "_any_trace")] trace::task_ready_begin(self, &task); if self.run_queue.enqueue(task, l) { @@ -432,7 +432,7 @@ impl SyncExecutor { .executor .store((self as *const Self).cast_mut(), Ordering::Relaxed); - #[cfg(feature = "trace")] + #[cfg(feature = "_any_trace")] trace::task_new(self, &task); state::locked(|l| { @@ -444,23 +444,23 @@ impl SyncExecutor { /// /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. pub(crate) unsafe fn poll(&'static self) { - #[cfg(feature = "trace")] + #[cfg(feature = "_any_trace")] trace::poll_start(self); self.run_queue.dequeue_all(|p| { let task = p.header(); - #[cfg(feature = "trace")] + #[cfg(feature = "_any_trace")] trace::task_exec_begin(self, &p); // Run the task task.poll_fn.get().unwrap_unchecked()(p); - #[cfg(feature = "trace")] + #[cfg(feature = "_any_trace")] trace::task_exec_end(self, &p); }); - #[cfg(feature = "trace")] + #[cfg(feature = "_any_trace")] trace::executor_idle(self) } } diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs index e769d63da..636608d02 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}; /// This static provides access to the global task tracker which maintains /// a list of all tasks in the system. It's automatically updated by the /// task lifecycle hooks in the trace module. -pub static TASK_TRACKER: TaskTracker = TaskTracker::new(); +#[cfg(feature = "rtos-trace")] +pub(crate) static TASK_TRACKER: TaskTracker = TaskTracker::new(); /// A thread-safe tracker for all tasks in the system /// /// This struct uses an intrusive linked list approach to track all tasks /// without additional memory allocations. It maintains a global list of /// tasks that can be traversed to find all currently existing tasks. -pub struct TaskTracker { +#[cfg(feature = "rtos-trace")] +pub(crate) struct TaskTracker { head: AtomicPtr, } +#[cfg(feature = "rtos-trace")] impl TaskTracker { /// Creates a new empty task tracker /// @@ -191,7 +194,7 @@ impl TaskRefTrace for TaskRef { } } -#[cfg(not(feature = "rtos-trace"))] +#[cfg(feature = "trace")] extern "Rust" { /// This callback is called when the executor begins polling. This will always /// be paired with a later call to `_embassy_trace_executor_idle`. @@ -253,7 +256,7 @@ extern "Rust" { #[inline] pub(crate) fn poll_start(executor: &SyncExecutor) { - #[cfg(not(feature = "rtos-trace"))] + #[cfg(feature = "trace")] unsafe { _embassy_trace_poll_start(executor as *const _ as u32) } @@ -261,7 +264,7 @@ pub(crate) fn poll_start(executor: &SyncExecutor) { #[inline] pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) { - #[cfg(not(feature = "rtos-trace"))] + #[cfg(feature = "trace")] unsafe { _embassy_trace_task_new(executor as *const _ as u32, task.as_ptr() as u32) } @@ -285,7 +288,7 @@ pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) { #[inline] pub(crate) fn task_end(executor: *const SyncExecutor, task: &TaskRef) { - #[cfg(not(feature = "rtos-trace"))] + #[cfg(feature = "trace")] unsafe { _embassy_trace_task_end(executor as u32, task.as_ptr() as u32) } @@ -293,7 +296,7 @@ pub(crate) fn task_end(executor: *const SyncExecutor, task: &TaskRef) { #[inline] pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) { - #[cfg(not(feature = "rtos-trace"))] + #[cfg(feature = "trace")] unsafe { _embassy_trace_task_ready_begin(executor as *const _ as u32, task.as_ptr() as u32) } @@ -303,7 +306,7 @@ pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) { #[inline] pub(crate) fn task_exec_begin(executor: &SyncExecutor, task: &TaskRef) { - #[cfg(not(feature = "rtos-trace"))] + #[cfg(feature = "trace")] unsafe { _embassy_trace_task_exec_begin(executor as *const _ as u32, task.as_ptr() as u32) } @@ -313,7 +316,7 @@ pub(crate) fn task_exec_begin(executor: &SyncExecutor, task: &TaskRef) { #[inline] pub(crate) fn task_exec_end(executor: &SyncExecutor, task: &TaskRef) { - #[cfg(not(feature = "rtos-trace"))] + #[cfg(feature = "trace")] unsafe { _embassy_trace_task_exec_end(executor as *const _ as u32, task.as_ptr() as u32) } @@ -323,7 +326,7 @@ pub(crate) fn task_exec_end(executor: &SyncExecutor, task: &TaskRef) { #[inline] pub(crate) fn executor_idle(executor: &SyncExecutor) { - #[cfg(not(feature = "rtos-trace"))] + #[cfg(feature = "trace")] unsafe { _embassy_trace_executor_idle(executor as *const _ as u32) } @@ -339,6 +342,7 @@ pub(crate) fn executor_idle(executor: &SyncExecutor) { /// /// # Returns /// An iterator that yields `TaskRef` items for each task +#[cfg(feature = "rtos-trace")] fn get_all_active_tasks() -> impl Iterator + 'static { struct TaskIterator<'a> { tracker: &'a TaskTracker, @@ -367,6 +371,7 @@ fn get_all_active_tasks() -> impl Iterator + 'static { } /// Perform an action on each active task +#[cfg(feature = "rtos-trace")] fn with_all_active_tasks(f: F) where F: FnMut(TaskRef), -- cgit From da9cdf0c536ec4fa7bdfb649750c44f70ef1cd55 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 9 Jul 2025 01:18:04 +0200 Subject: executor: add "task metadata" concept, make name a task metadata. --- embassy-executor/Cargo.toml | 7 ++++- embassy-executor/src/lib.rs | 3 ++ embassy-executor/src/metadata.rs | 56 ++++++++++++++++++++++++++++++++- embassy-executor/src/raw/mod.rs | 14 ++++++--- embassy-executor/src/raw/trace.rs | 29 +---------------- embassy-executor/src/spawner.rs | 66 ++++++++------------------------------- embassy-executor/tests/test.rs | 31 ++++++++++++++++++ 7 files changed, 119 insertions(+), 87 deletions(-) (limited to 'embassy-executor') diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index a36a61885..79680ae74 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -109,6 +109,11 @@ arch-avr = ["_arch", "dep:portable-atomic", "dep:avr-device"] ## spin (architecture agnostic; never sleeps) arch-spin = ["_arch"] +#! ### Metadata + +## Enable the `name` field in task metadata. +metadata-name = [] + #! ### Executor ## Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs) @@ -118,7 +123,7 @@ executor-interrupt = [] ## Enable tracing hooks trace = ["_any_trace"] ## Enable support for rtos-trace framework -rtos-trace = ["dep:rtos-trace", "_any_trace", "dep:embassy-time-driver"] +rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time-driver"] _any_trace = [] #! ### Timer Item Payload Size 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; mod spawner; pub use spawner::*; +mod metadata; +pub use metadata::*; + /// Implementation details for embassy macros. /// Do not use. Used for macros and HALs only. Not covered by semver guarantees. #[doc(hidden)] diff --git a/embassy-executor/src/metadata.rs b/embassy-executor/src/metadata.rs index 957417f6b..f92c9b37c 100644 --- a/embassy-executor/src/metadata.rs +++ b/embassy-executor/src/metadata.rs @@ -1 +1,55 @@ -pub struct Metadata {} +#[cfg(feature = "metadata-name")] +use core::cell::Cell; +use core::future::{poll_fn, Future}; +use core::task::Poll; + +#[cfg(feature = "metadata-name")] +use critical_section::Mutex; + +use crate::raw; + +/// Metadata associated with a task. +pub struct Metadata { + #[cfg(feature = "metadata-name")] + name: Mutex>>, +} + +impl Metadata { + pub(crate) const fn new() -> Self { + Self { + #[cfg(feature = "metadata-name")] + name: Mutex::new(Cell::new(None)), + } + } + + pub(crate) fn reset(&self) { + #[cfg(feature = "metadata-name")] + critical_section::with(|cs| self.name.borrow(cs).set(None)); + } + + /// Get the metadata for the current task. + /// + /// You can use this to read or modify the current task's metadata. + /// + /// This function is `async` just to get access to the current async + /// context. It returns instantly, it does not block/yield. + pub fn for_current_task() -> impl Future { + poll_fn(|cx| Poll::Ready(raw::task_from_waker(cx.waker()).metadata())) + } + + /// Get this task's name + /// + /// NOTE: this takes a critical section. + #[cfg(feature = "metadata-name")] + pub fn name(&self) -> Option<&'static str> { + critical_section::with(|cs| self.name.borrow(cs).get()) + } + + /// Set this task's name + /// + /// NOTE: this takes a critical section. + #[cfg(feature = "metadata-name")] + pub fn set_name(&self, name: &'static str) { + critical_section::with(|cs| self.name.borrow(cs).set(Some(name))) + } +} diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 87328df5a..a7e65360d 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -41,6 +41,7 @@ use self::state::State; use self::util::{SyncUnsafeCell, UninitCell}; pub use self::waker::task_from_waker; use super::SpawnToken; +use crate::Metadata; #[no_mangle] extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem { @@ -94,8 +95,9 @@ pub(crate) struct TaskHeader { /// Integrated timer queue storage. This field should not be accessed outside of the timer queue. pub(crate) timer_queue_item: TimerQueueItem, - #[cfg(feature = "_any_trace")] - pub(crate) name: Option<&'static str>, + + pub(crate) metadata: Metadata, + #[cfg(feature = "rtos-trace")] all_tasks_next: AtomicPtr, } @@ -127,6 +129,10 @@ impl TaskRef { unsafe { self.ptr.as_ref() } } + pub(crate) fn metadata(self) -> &'static Metadata { + unsafe { &self.ptr.as_ref().metadata } + } + /// Returns a reference to the executor that the task is currently running on. pub unsafe fn executor(self) -> Option<&'static Executor> { let executor = self.header().executor.load(Ordering::Relaxed); @@ -193,8 +199,7 @@ impl TaskStorage { poll_fn: SyncUnsafeCell::new(None), timer_queue_item: TimerQueueItem::new(), - #[cfg(feature = "_any_trace")] - name: None, + metadata: Metadata::new(), #[cfg(feature = "rtos-trace")] all_tasks_next: AtomicPtr::new(core::ptr::null_mut()), }, @@ -281,6 +286,7 @@ impl AvailableTask { fn initialize_impl(self, future: impl FnOnce() -> F) -> SpawnToken { unsafe { + self.task.raw.metadata.reset(); self.task.raw.poll_fn.set(Some(TaskStorage::::poll)); self.task.future.write_in_place(future); diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs index 636608d02..ab0c1b8b6 100644 --- a/embassy-executor/src/raw/trace.rs +++ b/embassy-executor/src/raw/trace.rs @@ -168,32 +168,6 @@ impl TaskTracker { } } -/// Extension trait for `TaskRef` that provides tracing functionality. -/// -/// This trait is only available when the `trace` feature is enabled. -/// It extends `TaskRef` with methods for accessing and modifying task identifiers -/// and names, which are useful for debugging, logging, and performance analysis. -pub trait TaskRefTrace { - /// Get the name for a task - fn name(&self) -> Option<&'static str>; - - /// Set the name for a task - fn set_name(&self, name: Option<&'static str>); -} - -impl TaskRefTrace for TaskRef { - fn name(&self) -> Option<&'static str> { - self.header().name - } - - fn set_name(&self, name: Option<&'static str>) { - unsafe { - let header_ptr = self.ptr.as_ptr() as *mut TaskHeader; - (*header_ptr).name = name; - } - } -} - #[cfg(feature = "trace")] extern "Rust" { /// This callback is called when the executor begins polling. This will always @@ -383,9 +357,8 @@ where impl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor { fn task_list() { with_all_active_tasks(|task| { - let name = task.name().unwrap_or("unnamed task\0"); let info = rtos_trace::TaskInfo { - name, + name: task.metadata().name().unwrap_or("unnamed task\0"), priority: 0, stack_base: 0, stack_size: 0, diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 7550e8ea4..cd2113a28 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -5,8 +5,7 @@ use core::sync::atomic::Ordering; use core::task::Poll; use super::raw; -#[cfg(feature = "trace")] -use crate::raw::trace::TaskRefTrace; +use crate::Metadata; /// Token to spawn a newly-created task in an executor. /// @@ -36,6 +35,14 @@ impl SpawnToken { } } + /// Return a SpawnToken that represents a failed spawn. + pub fn new_failed() -> Self { + Self { + raw_task: None, + phantom: PhantomData, + } + } + /// Returns the task ID if available, otherwise 0 /// This can be used in combination with rtos-trace to match task names with IDs pub fn id(&self) -> u32 { @@ -45,12 +52,10 @@ impl SpawnToken { } } - /// Return a SpawnToken that represents a failed spawn. - pub fn new_failed() -> Self { - Self { - raw_task: None, - phantom: PhantomData, - } + /// Get the metadata for this task. You can use this to set metadata fields + /// prior to spawning it. + pub fn metadata(&self) -> &Metadata { + self.raw_task.unwrap().metadata() } } @@ -198,51 +203,6 @@ impl Spawner { } } -/// Extension trait adding tracing capabilities to the Spawner -/// -/// This trait provides an additional method to spawn tasks with an associated name, -/// which can be useful for debugging and tracing purposes. -pub trait SpawnerTraceExt { - /// Spawns a new task with a specified name. - /// - /// # Arguments - /// * `name` - Static string name to associate with the task - /// * `token` - Token representing the task to spawn - /// - /// # Returns - /// Result indicating whether the spawn was successful - fn spawn_named(&self, name: &'static str, token: SpawnToken) -> Result<(), SpawnError>; -} - -/// Implementation of the SpawnerTraceExt trait for Spawner when trace is enabled -#[cfg(feature = "trace")] -impl SpawnerTraceExt for Spawner { - fn spawn_named(&self, name: &'static str, token: SpawnToken) -> Result<(), SpawnError> { - let task = token.raw_task; - core::mem::forget(token); - - match task { - Some(task) => { - // Set the name when trace is enabled - task.set_name(Some(name)); - - unsafe { self.executor.spawn(task) }; - Ok(()) - } - None => Err(SpawnError::Busy), - } - } -} - -/// Implementation of the SpawnerTraceExt trait for Spawner when trace is disabled -#[cfg(not(feature = "trace"))] -impl SpawnerTraceExt for Spawner { - fn spawn_named(&self, _name: &'static str, token: SpawnToken) -> Result<(), SpawnError> { - // When trace is disabled, just forward to regular spawn and ignore the name - self.spawn(token) - } -} - /// Handle to spawn tasks into an executor from any thread. /// /// This Spawner can be used from any thread (it is Send), but it can diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index b84d3785a..530314ac3 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -326,3 +326,34 @@ fn recursive_task() { spawner.spawn(task1()); } } + +#[cfg(feature = "metadata-name")] +#[test] +fn task_metadata() { + #[task] + async fn task1(expected_name: Option<&'static str>) { + use embassy_executor::Metadata; + assert_eq!(Metadata::for_current_task().await.name(), expected_name); + } + + // check no task name + let (executor, _) = setup(); + executor.spawner().spawn(task1(None)).unwrap(); + unsafe { executor.poll() }; + + // check setting task name + let token = task1(Some("foo")); + token.metadata().set_name("foo"); + executor.spawner().spawn(token).unwrap(); + unsafe { executor.poll() }; + + let token = task1(Some("bar")); + token.metadata().set_name("bar"); + executor.spawner().spawn(token).unwrap(); + unsafe { executor.poll() }; + + // check name is cleared if the task pool slot is recycled. + let (executor, _) = setup(); + executor.spawner().spawn(task1(None)).unwrap(); + unsafe { executor.poll() }; +} -- cgit From 34ff67cdbf25e278ff99bd4a05b6b8c6a30fa5d1 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 9 Jul 2025 01:18:47 +0200 Subject: executor: do not deref a mut ptr to the entire taskheader. --- embassy-executor/src/raw/trace.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'embassy-executor') diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs index ab0c1b8b6..e52960dc7 100644 --- a/embassy-executor/src/raw/trace.rs +++ b/embassy-executor/src/raw/trace.rs @@ -128,7 +128,7 @@ impl TaskTracker { /// # Arguments /// * `task` - The task reference to add to the tracker pub fn add(&self, task: TaskRef) { - let task_ptr = task.as_ptr() as *mut TaskHeader; + let task_ptr = task.as_ptr(); loop { let current_head = self.head.load(Ordering::Acquire); @@ -138,7 +138,7 @@ impl TaskTracker { if self .head - .compare_exchange(current_head, task_ptr, Ordering::Release, Ordering::Relaxed) + .compare_exchange(current_head, task_ptr.cast_mut(), Ordering::Release, Ordering::Relaxed) .is_ok() { break; -- cgit From 8aec341f28a00012e1771d5c35d2647e11830755 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 9 Jul 2025 01:49:31 +0200 Subject: executor: return error when creating the spawntoken, not when spawning. --- embassy-executor/src/raw/mod.rs | 18 +++--- embassy-executor/src/spawner.rs | 65 +++------------------- embassy-executor/tests/test.rs | 29 +++++----- .../tests/ui/return_impl_future_nonsend.rs | 2 +- .../tests/ui/return_impl_future_nonsend.stderr | 6 +- embassy-executor/tests/ui/return_impl_send.stderr | 2 +- embassy-executor/tests/ui/spawn_nonsend.rs | 2 +- embassy-executor/tests/ui/spawn_nonsend.stderr | 10 ++-- 8 files changed, 43 insertions(+), 91 deletions(-) (limited to 'embassy-executor') diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index a7e65360d..bdaa32951 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -41,7 +41,7 @@ use self::state::State; use self::util::{SyncUnsafeCell, UninitCell}; pub use self::waker::task_from_waker; use super::SpawnToken; -use crate::Metadata; +use crate::{Metadata, SpawnError}; #[no_mangle] extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem { @@ -220,11 +220,11 @@ impl TaskStorage { /// /// Once the task has finished running, you may spawn it again. It is allowed to spawn it /// on a different executor. - pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + pub fn spawn(&'static self, future: impl FnOnce() -> F) -> Result, SpawnError> { let task = AvailableTask::claim(self); match task { - Some(task) => task.initialize(future), - None => SpawnToken::new_failed(), + Some(task) => Ok(task.initialize(future)), + None => Err(SpawnError::Busy), } } @@ -353,10 +353,10 @@ impl TaskPool { } } - fn spawn_impl(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + fn spawn_impl(&'static self, future: impl FnOnce() -> F) -> Result, SpawnError> { match self.pool.iter().find_map(AvailableTask::claim) { - Some(task) => task.initialize_impl::(future), - None => SpawnToken::new_failed(), + Some(task) => Ok(task.initialize_impl::(future)), + None => Err(SpawnError::Busy), } } @@ -367,7 +367,7 @@ impl TaskPool { /// This will loop over the pool and spawn the task in the first storage that /// is currently free. If none is free, a "poisoned" SpawnToken is returned, /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error. - pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + pub fn spawn(&'static self, future: impl FnOnce() -> F) -> Result, SpawnError> { self.spawn_impl::(future) } @@ -380,7 +380,7 @@ impl TaskPool { /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn` /// is an `async fn`, NOT a hand-written `Future`. #[doc(hidden)] - pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken + pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> Result, SpawnError> where FutFn: FnOnce() -> F, { diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index cd2113a28..83d896b76 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -23,39 +23,28 @@ use crate::Metadata; /// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it. #[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"] pub struct SpawnToken { - pub(crate) raw_task: Option, + pub(crate) raw_task: raw::TaskRef, phantom: PhantomData<*mut S>, } impl SpawnToken { pub(crate) unsafe fn new(raw_task: raw::TaskRef) -> Self { Self { - raw_task: Some(raw_task), + raw_task, phantom: PhantomData, } } - /// Return a SpawnToken that represents a failed spawn. - pub fn new_failed() -> Self { - Self { - raw_task: None, - phantom: PhantomData, - } - } - - /// Returns the task ID if available, otherwise 0 + /// Returns the task ID. /// This can be used in combination with rtos-trace to match task names with IDs pub fn id(&self) -> u32 { - match self.raw_task { - None => 0, - Some(t) => t.id(), - } + self.raw_task.id() } /// Get the metadata for this task. You can use this to set metadata fields /// prior to spawning it. pub fn metadata(&self) -> &Metadata { - self.raw_task.unwrap().metadata() + self.raw_task.metadata() } } @@ -164,30 +153,10 @@ impl Spawner { /// Spawn a task into an executor. /// /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`). - pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { + pub fn spawn(&self, token: SpawnToken) { let task = token.raw_task; mem::forget(token); - - match task { - Some(task) => { - unsafe { self.executor.spawn(task) }; - Ok(()) - } - None => Err(SpawnError::Busy), - } - } - - // Used by the `embassy_executor_macros::main!` macro to throw an error when spawn - // fails. This is here to allow conditional use of `defmt::unwrap!` - // without introducing a `defmt` feature in the `embassy_executor_macros` package, - // which would require use of `-Z namespaced-features`. - /// Spawn a task into an executor, panicking on failure. - /// - /// # Panics - /// - /// Panics if the spawning fails. - pub fn must_spawn(&self, token: SpawnToken) { - unwrap!(self.spawn(token)); + unsafe { self.executor.spawn(task) } } /// Convert this Spawner to a SendSpawner. This allows you to send the @@ -245,25 +214,9 @@ impl SendSpawner { /// Spawn a task into an executor. /// /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`). - pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { + pub fn spawn(&self, token: SpawnToken) { let header = token.raw_task; mem::forget(token); - - match header { - Some(header) => { - unsafe { self.executor.spawn(header) }; - Ok(()) - } - None => Err(SpawnError::Busy), - } - } - - /// Spawn a task into an executor, panicking on failure. - /// - /// # Panics - /// - /// Panics if the spawning fails. - pub fn must_spawn(&self, token: SpawnToken) { - unwrap!(self.spawn(token)); + unsafe { self.executor.spawn(header) } } } diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 530314ac3..85c5dc1d9 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -65,7 +65,7 @@ fn executor_task() { } let (executor, trace) = setup(); - executor.spawner().spawn(task1(trace.clone())).unwrap(); + executor.spawner().spawn(task1(trace.clone()).unwrap()); unsafe { executor.poll() }; unsafe { executor.poll() }; @@ -93,7 +93,7 @@ fn executor_task_rpit() { } let (executor, trace) = setup(); - executor.spawner().spawn(task1(trace.clone())).unwrap(); + executor.spawner().spawn(task1(trace.clone()).unwrap()); unsafe { executor.poll() }; unsafe { executor.poll() }; @@ -120,7 +120,7 @@ fn executor_task_self_wake() { } let (executor, trace) = setup(); - executor.spawner().spawn(task1(trace.clone())).unwrap(); + executor.spawner().spawn(task1(trace.clone()).unwrap()); unsafe { executor.poll() }; unsafe { executor.poll() }; @@ -152,7 +152,7 @@ fn executor_task_self_wake_twice() { } let (executor, trace) = setup(); - executor.spawner().spawn(task1(trace.clone())).unwrap(); + executor.spawner().spawn(task1(trace.clone()).unwrap()); unsafe { executor.poll() }; unsafe { executor.poll() }; @@ -188,7 +188,7 @@ fn waking_after_completion_does_not_poll() { let waker = Box::leak(Box::new(AtomicWaker::new())); let (executor, trace) = setup(); - executor.spawner().spawn(task1(trace.clone(), waker)).unwrap(); + executor.spawner().spawn(task1(trace.clone(), waker).unwrap()); unsafe { executor.poll() }; waker.wake(); @@ -200,7 +200,7 @@ fn waking_after_completion_does_not_poll() { unsafe { executor.poll() }; // Clears running status // Can respawn waken-but-dead task - executor.spawner().spawn(task1(trace.clone(), waker)).unwrap(); + executor.spawner().spawn(task1(trace.clone(), waker).unwrap()); unsafe { executor.poll() }; @@ -250,7 +250,7 @@ fn waking_with_old_waker_after_respawn() { let waker = Box::leak(Box::new(AtomicWaker::new())); let (executor, trace) = setup(); - executor.spawner().spawn(task1(trace.clone(), waker)).unwrap(); + executor.spawner().spawn(task1(trace.clone(), waker).unwrap()); unsafe { executor.poll() }; unsafe { executor.poll() }; // progress to registering the waker @@ -273,8 +273,7 @@ fn waking_with_old_waker_after_respawn() { let (other_executor, other_trace) = setup(); other_executor .spawner() - .spawn(task1(other_trace.clone(), waker)) - .unwrap(); + .spawn(task1(other_trace.clone(), waker).unwrap()); unsafe { other_executor.poll() }; // just run to the yield_now waker.wake(); // trigger old waker registration @@ -338,22 +337,22 @@ fn task_metadata() { // check no task name let (executor, _) = setup(); - executor.spawner().spawn(task1(None)).unwrap(); + executor.spawner().spawn(task1(None).unwrap()); unsafe { executor.poll() }; // check setting task name - let token = task1(Some("foo")); + let token = task1(Some("foo")).unwrap(); token.metadata().set_name("foo"); - executor.spawner().spawn(token).unwrap(); + executor.spawner().spawn(token); unsafe { executor.poll() }; - let token = task1(Some("bar")); + let token = task1(Some("bar")).unwrap(); token.metadata().set_name("bar"); - executor.spawner().spawn(token).unwrap(); + executor.spawner().spawn(token); unsafe { executor.poll() }; // check name is cleared if the task pool slot is recycled. let (executor, _) = setup(); - executor.spawner().spawn(task1(None)).unwrap(); + executor.spawner().spawn(task1(None).unwrap()); unsafe { executor.poll() }; } diff --git a/embassy-executor/tests/ui/return_impl_future_nonsend.rs b/embassy-executor/tests/ui/return_impl_future_nonsend.rs index b8c184b21..77b3119d6 100644 --- a/embassy-executor/tests/ui/return_impl_future_nonsend.rs +++ b/embassy-executor/tests/ui/return_impl_future_nonsend.rs @@ -15,7 +15,7 @@ fn task() -> impl Future { } fn send_spawn(s: SendSpawner) { - s.spawn(task()).unwrap(); + s.spawn(task().unwrap()); } fn main() {} diff --git a/embassy-executor/tests/ui/return_impl_future_nonsend.stderr b/embassy-executor/tests/ui/return_impl_future_nonsend.stderr index 8aeb9738a..51944ad65 100644 --- a/embassy-executor/tests/ui/return_impl_future_nonsend.stderr +++ b/embassy-executor/tests/ui/return_impl_future_nonsend.stderr @@ -1,8 +1,8 @@ error: future cannot be sent between threads safely --> tests/ui/return_impl_future_nonsend.rs:18:13 | -18 | s.spawn(task()).unwrap(); - | ^^^^^^ future created by async block is not `Send` +18 | s.spawn(task().unwrap()); + | ^^^^^^^^^^^^^^^ future created by async block is not `Send` | = help: within `impl Sized`, the trait `Send` is not implemented for `*mut ()` note: captured value is not `Send` @@ -13,5 +13,5 @@ note: captured value is not `Send` note: required by a bound in `SendSpawner::spawn` --> src/spawner.rs | - | pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { + | pub fn spawn(&self, token: SpawnToken) { | ^^^^ required by this bound in `SendSpawner::spawn` diff --git a/embassy-executor/tests/ui/return_impl_send.stderr b/embassy-executor/tests/ui/return_impl_send.stderr index 759be1cde..5d19465ec 100644 --- a/embassy-executor/tests/ui/return_impl_send.stderr +++ b/embassy-executor/tests/ui/return_impl_send.stderr @@ -97,7 +97,7 @@ note: required by a bound in `TaskPool::::spawn` | impl TaskPool { | ^^^^^^ required by this bound in `TaskPool::::spawn` ... - | pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken { + | pub fn spawn(&'static self, future: impl FnOnce() -> F) -> Result, SpawnError> { | ----- required by a bound in this associated function = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/embassy-executor/tests/ui/spawn_nonsend.rs b/embassy-executor/tests/ui/spawn_nonsend.rs index 4c4cc7697..601041941 100644 --- a/embassy-executor/tests/ui/spawn_nonsend.rs +++ b/embassy-executor/tests/ui/spawn_nonsend.rs @@ -10,7 +10,7 @@ async fn task(non_send: *mut ()) { } fn send_spawn(s: SendSpawner) { - s.spawn(task(core::ptr::null_mut())).unwrap(); + s.spawn(task(core::ptr::null_mut()).unwrap()); } fn main() {} diff --git a/embassy-executor/tests/ui/spawn_nonsend.stderr b/embassy-executor/tests/ui/spawn_nonsend.stderr index 2a06c8b94..25bd7d78d 100644 --- a/embassy-executor/tests/ui/spawn_nonsend.stderr +++ b/embassy-executor/tests/ui/spawn_nonsend.stderr @@ -12,8 +12,8 @@ error[E0277]: `*mut ()` cannot be sent between threads safely 7 | #[embassy_executor::task] | ------------------------- within this `impl Sized` ... -13 | s.spawn(task(core::ptr::null_mut())).unwrap(); - | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*mut ()` cannot be sent between threads safely +13 | s.spawn(task(core::ptr::null_mut()).unwrap()); + | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*mut ()` cannot be sent between threads safely | | | required by a bound introduced by this call | @@ -26,8 +26,8 @@ note: required because it's used within this closure note: required because it appears within the type `impl Sized` --> src/raw/mod.rs | - | pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> SpawnToken - | ^^^^^^^^^^ + | pub unsafe fn _spawn_async_fn(&'static self, future: FutFn) -> Result, SpawnError> + | ^^^^^^^^^^ note: required because it appears within the type `impl Sized` --> tests/ui/spawn_nonsend.rs:7:1 | @@ -36,6 +36,6 @@ note: required because it appears within the type `impl Sized` note: required by a bound in `SendSpawner::spawn` --> src/spawner.rs | - | pub fn spawn(&self, token: SpawnToken) -> Result<(), SpawnError> { + | pub fn spawn(&self, token: SpawnToken) { | ^^^^ required by this bound in `SendSpawner::spawn` = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) -- cgit From 916dce55ea9f8341422eb6d55c17d0a0fcfedce0 Mon Sep 17 00:00:00 2001 From: diondokter Date: Fri, 29 Aug 2025 13:30:11 +0200 Subject: Fix test & rtos-trace --- embassy-executor/src/raw/trace.rs | 2 +- embassy-executor/tests/test.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'embassy-executor') diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs index e52960dc7..b3086948c 100644 --- a/embassy-executor/src/raw/trace.rs +++ b/embassy-executor/src/raw/trace.rs @@ -246,7 +246,7 @@ pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) { #[cfg(feature = "rtos-trace")] { rtos_trace::trace::task_new(task.as_ptr() as u32); - let name = task.name().unwrap_or("unnamed task\0"); + let name = task.metadata().name().unwrap_or("unnamed task\0"); let info = rtos_trace::TaskInfo { name, priority: 0, diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 85c5dc1d9..6baf3dc21 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -322,7 +322,7 @@ fn recursive_task() { #[embassy_executor::task(pool_size = 2)] async fn task1() { let spawner = unsafe { Spawner::for_current_executor().await }; - spawner.spawn(task1()); + spawner.spawn(task1().unwrap()); } } -- cgit From e2c34ac735888d25d57d3ea07e8915c2e112048c Mon Sep 17 00:00:00 2001 From: diondokter Date: Fri, 29 Aug 2025 13:58:16 +0200 Subject: Add to changelog --- embassy-executor/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) (limited to 'embassy-executor') diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index f301429c0..f2db46ef9 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- Added new metadata API for tasks + ## 0.9.0 - 2025-08-26 - Added `extern "Rust" fn __embassy_time_queue_item_from_waker` -- cgit