diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 5fbb8cf13c..314e0a960c 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added optional "earliest deadline first" EDF scheduling - Migrate `cortex-ar` to `aarch32-cpu`. The feature name `arch-cortex-ar` remains the same and legacy ARM architectures are not supported. +- Bump `cortex-ar` to v0.3 +- Added `__try_embassy_time_queue_item_from_waker` ## 0.9.1 - 2025-08-31 diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index ab845ed3b0..2b7560de65 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -49,6 +49,7 @@ use self::run_queue::{RunQueue, RunQueueItem}; use self::state::State; use self::util::{SyncUnsafeCell, UninitCell}; pub use self::waker::task_from_waker; +use self::waker::try_task_from_waker; use super::SpawnToken; use crate::{Metadata, SpawnError}; @@ -57,6 +58,11 @@ extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static unsafe { task_from_waker(waker).timer_queue_item() } } +#[unsafe(no_mangle)] +extern "Rust" fn __try_embassy_time_queue_item_from_waker(waker: &Waker) -> Option<&'static mut TimerQueueItem> { + unsafe { try_task_from_waker(waker).map(|task| task.timer_queue_item()) } +} + /// Raw task header for use in task pointers. /// /// A task can be in one of the following states: diff --git a/embassy-executor/src/raw/waker.rs b/embassy-executor/src/raw/waker.rs index 2706f0fdff..8416f9f934 100644 --- a/embassy-executor/src/raw/waker.rs +++ b/embassy-executor/src/raw/waker.rs @@ -32,13 +32,18 @@ pub(crate) unsafe fn from_task(p: TaskRef) -> Waker { /// /// Panics if the waker is not created by the Embassy executor. pub fn task_from_waker(waker: &Waker) -> TaskRef { + unwrap!( + try_task_from_waker(waker), + "Found waker not created by the Embassy executor. Unless the generic timer queue is enabled, `embassy_time::Timer` only works with the Embassy executor." + ) +} + +pub(crate) fn try_task_from_waker(waker: &Waker) -> Option { // make sure to compare vtable addresses. Doing `==` on the references // will compare the contents, which is slower. if waker.vtable() as *const _ != &VTABLE as *const _ { - panic!( - "Found waker not created by the Embassy executor. `embassy_time::Timer` only works with the Embassy executor." - ) + return None; } // safety: our wakers are always created with `TaskRef::as_ptr` - unsafe { TaskRef::from_ptr(waker.data() as *const TaskHeader) } + Some(unsafe { TaskRef::from_ptr(waker.data() as *const TaskHeader) }) } diff --git a/embassy-executor/src/raw/waker_turbo.rs b/embassy-executor/src/raw/waker_turbo.rs index 919bcc61a7..ee33e7633d 100644 --- a/embassy-executor/src/raw/waker_turbo.rs +++ b/embassy-executor/src/raw/waker_turbo.rs @@ -25,6 +25,10 @@ pub fn task_from_waker(waker: &Waker) -> TaskRef { unsafe { TaskRef::from_ptr(ptr as *const TaskHeader) } } +pub(crate) fn try_task_from_waker(waker: &Waker) -> Option { + Some(task_from_waker(waker)) +} + #[inline(never)] #[unsafe(no_mangle)] fn _turbo_wake(ptr: NonNull<()>) { diff --git a/embassy-time-queue-utils/CHANGELOG.md b/embassy-time-queue-utils/CHANGELOG.md index 03d89f9a74..6970c3bce1 100644 --- a/embassy-time-queue-utils/CHANGELOG.md +++ b/embassy-time-queue-utils/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- Both integrated and generic queue implementations are available for use, independent of their respective features. +- Queue methods are now `#[inline]` + ## 0.3.0 - 2025-08-26 ## 0.2.1 - 2025-08-26 diff --git a/embassy-time-queue-utils/src/lib.rs b/embassy-time-queue-utils/src/lib.rs index a6f66913fd..61d57d66bf 100644 --- a/embassy-time-queue-utils/src/lib.rs +++ b/embassy-time-queue-utils/src/lib.rs @@ -2,12 +2,46 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] -#[cfg(feature = "_generic-queue")] +use core::task::Waker; + pub mod queue_generic; -#[cfg(not(feature = "_generic-queue"))] pub mod queue_integrated; #[cfg(feature = "_generic-queue")] -pub use queue_generic::Queue; +type QueueImpl = queue_generic::Queue; #[cfg(not(feature = "_generic-queue"))] -pub use queue_integrated::Queue; +type QueueImpl = queue_integrated::Queue; + +/// The default timer queue, configured by the crate's features. +/// +/// If any of the `generic-queue-X` features are enabled, this implements a generic +/// timer queue of capacity X. Otherwise, it implements an integrated timer queue. +#[derive(Debug)] +pub struct Queue { + queue: QueueImpl, +} + +impl Queue { + /// Creates a new timer queue. + #[inline] + pub const fn new() -> Self { + Self { + queue: QueueImpl::new(), + } + } + + /// Schedules a task to run at a specific time, and returns whether any changes were made. + /// + /// If this function returns `true`, the called should find the next expiration time and set + /// a new alarm for that time. + #[inline] + pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool { + self.queue.schedule_wake(at, waker) + } + + /// Dequeues expired timers and returns the next alarm time. + #[inline] + pub fn next_expiration(&mut self, now: u64) -> u64 { + self.queue.next_expiration(now) + } +} diff --git a/embassy-time-queue-utils/src/queue_generic.rs b/embassy-time-queue-utils/src/queue_generic.rs index 88986953db..6dba286f23 100644 --- a/embassy-time-queue-utils/src/queue_generic.rs +++ b/embassy-time-queue-utils/src/queue_generic.rs @@ -41,6 +41,7 @@ pub struct ConstGenericQueue { impl ConstGenericQueue { /// Creates a new timer queue. + #[inline] pub const fn new() -> Self { Self { queue: Vec::new() } } @@ -49,6 +50,7 @@ impl ConstGenericQueue { /// /// If this function returns `true`, the called should find the next expiration time and set /// a new alarm for that time. + #[inline] pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool { self.queue .iter_mut() @@ -81,6 +83,7 @@ impl ConstGenericQueue { } /// Dequeues expired timers and returns the next alarm time. + #[inline] pub fn next_expiration(&mut self, now: u64) -> u64 { let mut next_alarm = u64::MAX; @@ -127,6 +130,7 @@ pub struct Queue { impl Queue { /// Creates a new timer queue. + #[inline] pub const fn new() -> Self { Self { queue: ConstGenericQueue::new(), @@ -137,11 +141,13 @@ impl Queue { /// /// If this function returns `true`, the called should find the next expiration time and set /// a new alarm for that time. + #[inline] pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool { self.queue.schedule_wake(at, waker) } /// Dequeues expired timers and returns the next alarm time. + #[inline] pub fn next_expiration(&mut self, now: u64) -> u64 { self.queue.next_expiration(now) } diff --git a/embassy-time-queue-utils/src/queue_integrated.rs b/embassy-time-queue-utils/src/queue_integrated.rs index 2731d1ac62..fd447fde1d 100644 --- a/embassy-time-queue-utils/src/queue_integrated.rs +++ b/embassy-time-queue-utils/src/queue_integrated.rs @@ -47,6 +47,7 @@ unsafe impl Sync for Queue {} impl Queue { /// Creates a new timer queue. + #[inline] pub const fn new() -> Self { Self { head: Cell::new(None) } } @@ -55,12 +56,22 @@ impl Queue { /// /// If this function returns `true`, the called should find the next expiration time and set /// a new alarm for that time. + #[inline] pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool { let item = unsafe { // Safety: the `&mut self`, along with the Safety note of the Queue, are sufficient to // ensure that this function creates the only mutable reference to the queue item. TimerQueueItem::from_embassy_waker(waker) }; + self.schedule_wake_queue_item(at, item, waker) + } + + /// Schedules a task to run at a specific time, using its integrated queue item. + /// + /// If this function returns `true`, the called should find the next expiration time and set + /// a new alarm for that time. + #[inline] + pub fn schedule_wake_queue_item(&mut self, at: u64, item: &mut TimerQueueItem, waker: &Waker) -> bool { let item = unsafe { item.as_mut::() }; match item.waker.as_ref() { Some(_) if at <= item.expires_at => { @@ -98,6 +109,7 @@ impl Queue { /// /// The provided callback will be called for each expired task. Tasks that never expire /// will be removed, but the callback will not be called. + #[inline] pub fn next_expiration(&mut self, now: u64) -> u64 { let mut next_expiration = u64::MAX;