embassy_executor_timer_queue/
lib.rs

1//! Timer queue item for embassy-executor integrated timer queues
2//!
3//! `embassy-executor` provides the memory needed to implement integrated timer queues. This crate
4//! exists to separate that memory from `embassy-executor` itself, to decouple the timer queue's
5//! release cycle from `embassy-executor`.
6//!
7//! This crate contains two things:
8//! - [`TimerQueueItem`]: The item type that can be requested from the executor. The size of this
9//!   type can be configured using the `timer-item-size-N-words` Cargo features.
10//! - The expectation that `extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &mut TimerQueueItem`
11//!   is implemented (by `embassy-executor`, most likely). This function must return a mutable
12//!   reference to the `TimerQueueItem` associated with the given waker.
13//!
14//! As a queue implementor, you will need to choose one of the `timer-item-size-N-words` features to
15//! select a queue item size. You can then define your own item type, which must be
16//! `#[repr(align(8))]` (or less) and must fit into the size you selected.
17//!
18//! You can access the `TimerQueueItem` from a `Waker` using the [`from_embassy_waker`](TimerQueueItem::from_embassy_waker)
19//! method. You can then use the [`as_ref`](TimerQueueItem::as_ref) and [`as_mut`](TimerQueueItem::as_mut)
20//! methods to reinterpret the data stored in the item as your custom item type.
21#![no_std]
22
23use core::task::Waker;
24
25const ITEM_WORDS: usize = if cfg!(feature = "timer-item-size-8-words") {
26    8
27} else if cfg!(feature = "timer-item-size-6-words") {
28    6
29} else if cfg!(feature = "timer-item-size-4-words") {
30    4
31} else {
32    0
33};
34
35/// The timer queue item provided by the executor.
36///
37/// This type is opaque, it only provides the raw storage for a queue item. The queue implementation
38/// is responsible for reinterpreting the contents of the item using [`TimerQueueItem::as_ref`] and
39/// [`TimerQueueItem::as_mut`].
40#[repr(align(8))]
41pub struct TimerQueueItem {
42    data: [usize; ITEM_WORDS],
43}
44
45impl TimerQueueItem {
46    /// Creates a new, zero-initialized `TimerQueueItem`.
47    pub const fn new() -> Self {
48        Self { data: [0; ITEM_WORDS] }
49    }
50
51    /// Retrieves the `TimerQueueItem` reference that belongs to the task of the waker.
52    ///
53    /// Panics if called with a non-embassy waker.
54    ///
55    /// # Safety
56    ///
57    /// The caller must ensure they are not violating Rust's aliasing rules - it is not allowed
58    /// to use this method to create multiple mutable references to the same `TimerQueueItem` at
59    /// the same time.
60    ///
61    /// This function must only be called in the context of a timer queue implementation.
62    pub unsafe fn from_embassy_waker(waker: &Waker) -> &'static mut Self {
63        unsafe extern "Rust" {
64            // Waker -> TimerQueueItem, validates that Waker is an embassy Waker.
65            fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem;
66        }
67        unsafe { __embassy_time_queue_item_from_waker(waker) }
68    }
69
70    /// Access the data as a reference to a type `T`.
71    ///
72    /// Safety:
73    ///
74    /// - The type must be valid when zero-initialized.
75    /// - The timer queue should only be interpreted as a single type `T` during its lifetime.
76    pub unsafe fn as_ref<T>(&self) -> &T {
77        const { validate::<T>() }
78        unsafe { &*(self.data.as_ptr() as *const T) }
79    }
80
81    /// Access the data as a reference to a type `T`.
82    ///
83    /// Safety:
84    ///
85    /// - The type must be valid when zero-initialized.
86    /// - The timer queue should only be interpreted as a single type `T` during its lifetime.
87    pub unsafe fn as_mut<T>(&self) -> &mut T {
88        const { validate::<T>() }
89        unsafe { &mut *(self.data.as_ptr() as *mut T) }
90    }
91}
92
93const fn validate<T>() {
94    const {
95        assert!(
96            core::mem::size_of::<TimerQueueItem>() >= core::mem::size_of::<T>(),
97            "embassy-executor-timer-queue item size is smaller than the requested type. Select a larger timer-item-size-N-words feature."
98        );
99        assert!(
100            core::mem::align_of::<TimerQueueItem>() >= core::mem::align_of::<T>(),
101            "the alignment of the requested type is greater than 8"
102        );
103    }
104}