Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions embassy-executor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions embassy-executor/src/raw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand All @@ -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:
Expand Down
13 changes: 9 additions & 4 deletions embassy-executor/src/raw/waker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TaskRef> {
// 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) })
}
4 changes: 4 additions & 0 deletions embassy-executor/src/raw/waker_turbo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TaskRef> {
Some(task_from_waker(waker))
}

#[inline(never)]
#[unsafe(no_mangle)]
fn _turbo_wake(ptr: NonNull<()>) {
Expand Down
3 changes: 3 additions & 0 deletions embassy-time-queue-utils/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<!-- next-header -->
## 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
Expand Down
42 changes: 38 additions & 4 deletions embassy-time-queue-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
6 changes: 6 additions & 0 deletions embassy-time-queue-utils/src/queue_generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub struct ConstGenericQueue<const QUEUE_SIZE: usize> {

impl<const QUEUE_SIZE: usize> ConstGenericQueue<QUEUE_SIZE> {
/// Creates a new timer queue.
#[inline]
pub const fn new() -> Self {
Self { queue: Vec::new() }
}
Expand All @@ -49,6 +50,7 @@ impl<const QUEUE_SIZE: usize> ConstGenericQueue<QUEUE_SIZE> {
///
/// 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()
Expand Down Expand Up @@ -81,6 +83,7 @@ impl<const QUEUE_SIZE: usize> ConstGenericQueue<QUEUE_SIZE> {
}

/// 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;

Expand Down Expand Up @@ -127,6 +130,7 @@ pub struct Queue {

impl Queue {
/// Creates a new timer queue.
#[inline]
pub const fn new() -> Self {
Self {
queue: ConstGenericQueue::new(),
Expand All @@ -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)
}
Expand Down
12 changes: 12 additions & 0 deletions embassy-time-queue-utils/src/queue_integrated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) }
}
Expand All @@ -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::<QueueItem>() };
match item.waker.as_ref() {
Some(_) if at <= item.expires_at => {
Expand Down Expand Up @@ -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;

Expand Down