//! Timer queue item for embassy-executor integrated timer queues //! //! `embassy-executor` provides the memory needed to implement integrated timer queues. This crate //! exists to separate that memory from `embassy-executor` itself, to decouple the timer queue's //! release cycle from `embassy-executor`. //! //! This crate contains two things: //! - [`TimerQueueItem`]: The item type that can be requested from the executor. The size of this //! type can be configured using the `timer-item-size-N-words` Cargo features. //! - The expectation that `extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &mut TimerQueueItem` //! is implemented (by `embassy-executor`, most likely). This function must return a mutable //! reference to the `TimerQueueItem` associated with the given waker. //! //! As a queue implementor, you will need to choose one of the `timer-item-size-N-words` features to //! select a queue item size. You can then define your own item type, which must be //! `#[repr(align(8))]` (or less) and must fit into the size you selected. //! //! You can access the `TimerQueueItem` from a `Waker` using the [`from_embassy_waker`](TimerQueueItem::from_embassy_waker) //! method. You can then use the [`as_ref`](TimerQueueItem::as_ref) and [`as_mut`](TimerQueueItem::as_mut) //! methods to reinterpret the data stored in the item as your custom item type. #![no_std] use core::task::Waker; const ITEM_WORDS: usize = if cfg!(feature = "timer-item-size-8-words") { 8 } else if cfg!(feature = "timer-item-size-6-words") { 6 } else if cfg!(feature = "timer-item-size-4-words") { 4 } else { 0 }; /// The timer queue item provided by the executor. /// /// This type is opaque, it only provides the raw storage for a queue item. The queue implementation /// is responsible for reinterpreting the contents of the item using [`TimerQueueItem::as_ref`] and /// [`TimerQueueItem::as_mut`]. #[repr(align(8))] pub struct TimerQueueItem { data: [usize; ITEM_WORDS], } impl TimerQueueItem { /// Creates a new, zero-initialized `TimerQueueItem`. pub const fn new() -> Self { Self { data: [0; ITEM_WORDS] } } /// Retrieves the `TimerQueueItem` reference that belongs to the task of the waker. /// /// Panics if called with a non-embassy waker. /// /// # Safety /// /// The caller must ensure they are not violating Rust's aliasing rules - it is not allowed /// to use this method to create multiple mutable references to the same `TimerQueueItem` at /// the same time. /// /// This function must only be called in the context of a timer queue implementation. pub unsafe fn from_embassy_waker(waker: &Waker) -> &'static mut Self { unsafe extern "Rust" { // Waker -> TimerQueueItem, validates that Waker is an embassy Waker. fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem; } unsafe { __embassy_time_queue_item_from_waker(waker) } } /// Access the data as a reference to a type `T`. /// /// Safety: /// /// - The type must be valid when zero-initialized. /// - The timer queue should only be interpreted as a single type `T` during its lifetime. pub unsafe fn as_ref(&self) -> &T { const { validate::() } unsafe { &*(self.data.as_ptr() as *const T) } } /// Access the data as a reference to a type `T`. /// /// Safety: /// /// - The type must be valid when zero-initialized. /// - The timer queue should only be interpreted as a single type `T` during its lifetime. pub unsafe fn as_mut(&self) -> &mut T { const { validate::() } unsafe { &mut *(self.data.as_ptr() as *mut T) } } } const fn validate() { const { assert!( core::mem::size_of::() >= core::mem::size_of::(), "embassy-executor-timer-queue item size is smaller than the requested type. Select a larger timer-item-size-N-words feature." ); assert!( core::mem::align_of::() >= core::mem::align_of::(), "the alignment of the requested type is greater than 8" ); } }