mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-09-27 20:30:29 +00:00
executor: add "task metadata" concept, make name a task metadata.
This commit is contained in:
parent
9db0634f0f
commit
20822e8d65
2
.github/ci/test.sh
vendored
2
.github/ci/test.sh
vendored
@ -12,7 +12,7 @@ export CARGO_TARGET_DIR=/ci/cache/target
|
|||||||
# used when pointing stm32-metapac to a CI-built one.
|
# used when pointing stm32-metapac to a CI-built one.
|
||||||
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
export CARGO_NET_GIT_FETCH_WITH_CLI=true
|
||||||
|
|
||||||
cargo test --manifest-path ./embassy-executor/Cargo.toml
|
cargo test --manifest-path ./embassy-executor/Cargo.toml --features metadata-name
|
||||||
cargo test --manifest-path ./embassy-futures/Cargo.toml
|
cargo test --manifest-path ./embassy-futures/Cargo.toml
|
||||||
cargo test --manifest-path ./embassy-sync/Cargo.toml
|
cargo test --manifest-path ./embassy-sync/Cargo.toml
|
||||||
cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
|
cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
|
||||||
|
1
ci.sh
1
ci.sh
@ -31,6 +31,7 @@ cargo batch \
|
|||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt,arch-cortex-m,executor-thread,executor-interrupt \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt,arch-cortex-m,executor-thread,executor-interrupt \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m \
|
||||||
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,metadata-name \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,trace \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,trace \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,rtos-trace \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,rtos-trace \
|
||||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,trace,rtos-trace \
|
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,trace,rtos-trace \
|
||||||
|
@ -109,6 +109,11 @@ arch-avr = ["_arch", "dep:portable-atomic", "dep:avr-device"]
|
|||||||
## spin (architecture agnostic; never sleeps)
|
## spin (architecture agnostic; never sleeps)
|
||||||
arch-spin = ["_arch"]
|
arch-spin = ["_arch"]
|
||||||
|
|
||||||
|
#! ### Metadata
|
||||||
|
|
||||||
|
## Enable the `name` field in task metadata.
|
||||||
|
metadata-name = []
|
||||||
|
|
||||||
#! ### Executor
|
#! ### Executor
|
||||||
|
|
||||||
## Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
|
## Enable the thread-mode executor (using WFE/SEV in Cortex-M, WFI in other embedded archs)
|
||||||
@ -118,5 +123,5 @@ executor-interrupt = []
|
|||||||
## Enable tracing hooks
|
## Enable tracing hooks
|
||||||
trace = ["_any_trace"]
|
trace = ["_any_trace"]
|
||||||
## Enable support for rtos-trace framework
|
## 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 = []
|
_any_trace = []
|
||||||
|
@ -54,6 +54,9 @@ pub mod raw;
|
|||||||
mod spawner;
|
mod spawner;
|
||||||
pub use spawner::*;
|
pub use spawner::*;
|
||||||
|
|
||||||
|
mod metadata;
|
||||||
|
pub use metadata::*;
|
||||||
|
|
||||||
/// Implementation details for embassy macros.
|
/// Implementation details for embassy macros.
|
||||||
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
|
/// Do not use. Used for macros and HALs only. Not covered by semver guarantees.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -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<Cell<Option<&'static str>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Output = &'static Self> {
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -41,6 +41,7 @@ use self::state::State;
|
|||||||
use self::util::{SyncUnsafeCell, UninitCell};
|
use self::util::{SyncUnsafeCell, UninitCell};
|
||||||
pub use self::waker::task_from_waker;
|
pub use self::waker::task_from_waker;
|
||||||
use super::SpawnToken;
|
use super::SpawnToken;
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem {
|
extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem {
|
||||||
@ -94,8 +95,8 @@ pub(crate) struct TaskHeader {
|
|||||||
|
|
||||||
/// Integrated timer queue storage. This field should not be accessed outside of the timer queue.
|
/// Integrated timer queue storage. This field should not be accessed outside of the timer queue.
|
||||||
pub(crate) timer_queue_item: TimerQueueItem,
|
pub(crate) timer_queue_item: TimerQueueItem,
|
||||||
#[cfg(feature = "_any_trace")]
|
pub(crate) metadata: Metadata,
|
||||||
pub(crate) name: Option<&'static str>,
|
|
||||||
#[cfg(feature = "rtos-trace")]
|
#[cfg(feature = "rtos-trace")]
|
||||||
all_tasks_next: AtomicPtr<TaskHeader>,
|
all_tasks_next: AtomicPtr<TaskHeader>,
|
||||||
}
|
}
|
||||||
@ -127,6 +128,10 @@ impl TaskRef {
|
|||||||
unsafe { self.ptr.as_ref() }
|
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.
|
/// Returns a reference to the executor that the task is currently running on.
|
||||||
pub unsafe fn executor(self) -> Option<&'static Executor> {
|
pub unsafe fn executor(self) -> Option<&'static Executor> {
|
||||||
let executor = self.header().executor.load(Ordering::Relaxed);
|
let executor = self.header().executor.load(Ordering::Relaxed);
|
||||||
@ -193,8 +198,7 @@ impl<F: Future + 'static> TaskStorage<F> {
|
|||||||
poll_fn: SyncUnsafeCell::new(None),
|
poll_fn: SyncUnsafeCell::new(None),
|
||||||
|
|
||||||
timer_queue_item: TimerQueueItem::new(),
|
timer_queue_item: TimerQueueItem::new(),
|
||||||
#[cfg(feature = "_any_trace")]
|
metadata: Metadata::new(),
|
||||||
name: None,
|
|
||||||
#[cfg(feature = "rtos-trace")]
|
#[cfg(feature = "rtos-trace")]
|
||||||
all_tasks_next: AtomicPtr::new(core::ptr::null_mut()),
|
all_tasks_next: AtomicPtr::new(core::ptr::null_mut()),
|
||||||
},
|
},
|
||||||
@ -281,6 +285,7 @@ impl<F: Future + 'static> AvailableTask<F> {
|
|||||||
|
|
||||||
fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
|
fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
self.task.raw.metadata.reset();
|
||||||
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
|
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
|
||||||
self.task.future.write_in_place(future);
|
self.task.future.write_in_place(future);
|
||||||
|
|
||||||
|
@ -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")]
|
#[cfg(feature = "trace")]
|
||||||
extern "Rust" {
|
extern "Rust" {
|
||||||
/// This callback is called when the executor begins polling. This will always
|
/// 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 {
|
impl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor {
|
||||||
fn task_list() {
|
fn task_list() {
|
||||||
with_all_active_tasks(|task| {
|
with_all_active_tasks(|task| {
|
||||||
let name = task.name().unwrap_or("unnamed task\0");
|
|
||||||
let info = rtos_trace::TaskInfo {
|
let info = rtos_trace::TaskInfo {
|
||||||
name,
|
name: task.metadata().name().unwrap_or("unnamed task\0"),
|
||||||
priority: 0,
|
priority: 0,
|
||||||
stack_base: 0,
|
stack_base: 0,
|
||||||
stack_size: 0,
|
stack_size: 0,
|
||||||
|
@ -5,8 +5,7 @@ use core::sync::atomic::Ordering;
|
|||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
use super::raw;
|
use super::raw;
|
||||||
#[cfg(feature = "trace")]
|
use crate::Metadata;
|
||||||
use crate::raw::trace::TaskRefTrace;
|
|
||||||
|
|
||||||
/// Token to spawn a newly-created task in an executor.
|
/// Token to spawn a newly-created task in an executor.
|
||||||
///
|
///
|
||||||
@ -36,6 +35,14 @@ impl<S> SpawnToken<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 if available, otherwise 0
|
||||||
/// This can be used in combination with rtos-trace to match task names with IDs
|
/// This can be used in combination with rtos-trace to match task names with IDs
|
||||||
pub fn id(&self) -> u32 {
|
pub fn id(&self) -> u32 {
|
||||||
@ -45,12 +52,10 @@ impl<S> SpawnToken<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a SpawnToken that represents a failed spawn.
|
/// Get the metadata for this task. You can use this to set metadata fields
|
||||||
pub fn new_failed() -> Self {
|
/// prior to spawning it.
|
||||||
Self {
|
pub fn metadata(&self) -> &Metadata {
|
||||||
raw_task: None,
|
self.raw_task.unwrap().metadata()
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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<S>(&self, name: &'static str, token: SpawnToken<S>) -> Result<(), SpawnError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implementation of the SpawnerTraceExt trait for Spawner when trace is enabled
|
|
||||||
#[cfg(feature = "trace")]
|
|
||||||
impl SpawnerTraceExt for Spawner {
|
|
||||||
fn spawn_named<S>(&self, name: &'static str, token: SpawnToken<S>) -> 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<S>(&self, _name: &'static str, token: SpawnToken<S>) -> 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.
|
/// Handle to spawn tasks into an executor from any thread.
|
||||||
///
|
///
|
||||||
/// This Spawner can be used from any thread (it is Send), but it can
|
/// This Spawner can be used from any thread (it is Send), but it can
|
||||||
|
@ -326,3 +326,34 @@ fn recursive_task() {
|
|||||||
spawner.spawn(task1());
|
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() };
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user