aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDániel Buga <[email protected]>2025-08-04 00:05:25 +0200
committerDániel Buga <[email protected]>2025-08-18 12:50:51 +0200
commit74037f04933f4ec9a678e0b47fd6819e7c0489a9 (patch)
treecb81e7394b6b59324a08eb92eba6fd0d8d9f2a9c
parenta5cb04bdab602bc3bd056d254a9d61cad55bd967 (diff)
Make TimerQueueItem opaque
-rw-r--r--embassy-executor-timer-queue/CHANGELOG.md10
-rw-r--r--embassy-executor-timer-queue/Cargo.toml35
-rw-r--r--embassy-executor-timer-queue/README.md10
-rw-r--r--embassy-executor-timer-queue/src/lib.rs97
-rw-r--r--embassy-executor/CHANGELOG.md5
-rw-r--r--embassy-executor/Cargo.toml17
-rw-r--r--embassy-executor/src/raw/mod.rs33
-rw-r--r--embassy-executor/src/raw/timer_queue.rs73
-rw-r--r--embassy-time-queue-utils/CHANGELOG.md8
-rw-r--r--embassy-time-queue-utils/Cargo.toml2
-rw-r--r--embassy-time-queue-utils/src/lib.rs1
-rw-r--r--embassy-time-queue-utils/src/queue_integrated.rs122
12 files changed, 267 insertions, 146 deletions
diff --git a/embassy-executor-timer-queue/CHANGELOG.md b/embassy-executor-timer-queue/CHANGELOG.md
new file mode 100644
index 000000000..d43e0060d
--- /dev/null
+++ b/embassy-executor-timer-queue/CHANGELOG.md
@@ -0,0 +1,10 @@
1# Changelog for embassy-time-queue-utils
2
3All notable changes to this project will be documented in this file.
4
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
8## Unreeased
9
10- Initial implementation
diff --git a/embassy-executor-timer-queue/Cargo.toml b/embassy-executor-timer-queue/Cargo.toml
new file mode 100644
index 000000000..0db327ba9
--- /dev/null
+++ b/embassy-executor-timer-queue/Cargo.toml
@@ -0,0 +1,35 @@
1[package]
2name = "embassy-executor-timer-queue"
3version = "0.1.0"
4edition = "2021"
5description = "Timer queue item and interface between embassy-executor and timer queues"
6repository = "https://github.com/embassy-rs/embassy"
7documentation = "https://docs.embassy.dev/embassy-executor-timer-queue"
8readme = "README.md"
9license = "MIT OR Apache-2.0"
10categories = [
11 "embedded",
12 "no-std",
13 "concurrency",
14 "asynchronous",
15]
16
17[dependencies]
18
19[features]
20#! ### Timer Queue Item Size
21#! Sets the size of the timer items.
22
23## 4 words
24timer-item-size-4-words = []
25
26## 6 words
27timer-item-size-6-words = []
28
29## 8 words
30timer-item-size-8-words = []
31
32[package.metadata.embassy_docs]
33src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-timer-queue-v$VERSION/embassy-executor-timer-queue/src/"
34src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor-timer-queue/src/"
35target = "x86_64-unknown-linux-gnu"
diff --git a/embassy-executor-timer-queue/README.md b/embassy-executor-timer-queue/README.md
new file mode 100644
index 000000000..602aca7b1
--- /dev/null
+++ b/embassy-executor-timer-queue/README.md
@@ -0,0 +1,10 @@
1# embassy-executor-time-queue
2
3This crate defines the timer queue item that embassy-executor provides, and a way to access it, for
4executor-integrated timer queues. The crate decouples the release cycle of embassy-executor from
5that of the queue implementations'.
6
7As a HAL implementer, you only need to depend on this crate if you want to implement executor-integrated
8timer queues yourself, without using [`embassy-time-queue-utils`](https://crates.io/crates/embassy-time-queue-utils).
9
10As a HAL user, you should not need to depend on this crate.
diff --git a/embassy-executor-timer-queue/src/lib.rs b/embassy-executor-timer-queue/src/lib.rs
new file mode 100644
index 000000000..456ccaec3
--- /dev/null
+++ b/embassy-executor-timer-queue/src/lib.rs
@@ -0,0 +1,97 @@
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_SIZE: 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_SIZE],
43}
44
45impl TimerQueueItem {
46 /// Creates a new, zero-initialized `TimerQueueItem`.
47 pub const fn new() -> Self {
48 Self { data: [0; ITEM_SIZE] }
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 {
78 assert!(core::mem::size_of::<Self>() >= core::mem::size_of::<T>());
79 assert!(core::mem::align_of::<Self>() >= core::mem::align_of::<T>());
80 }
81 unsafe { &*(self.data.as_ptr() as *const T) }
82 }
83
84 /// Access the data as a reference to a type `T`.
85 ///
86 /// Safety:
87 ///
88 /// - The type must be valid when zero-initialized.
89 /// - The timer queue should only be interpreted as a single type `T` during its lifetime.
90 pub unsafe fn as_mut<T>(&self) -> &mut T {
91 const {
92 assert!(core::mem::size_of::<Self>() >= core::mem::size_of::<T>());
93 assert!(core::mem::align_of::<Self>() >= core::mem::align_of::<T>());
94 }
95 unsafe { &mut *(self.data.as_ptr() as *mut T) }
96 }
97}
diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md
index c35ad10f3..e2214b8ef 100644
--- a/embassy-executor/CHANGELOG.md
+++ b/embassy-executor/CHANGELOG.md
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Added `extern "Rust" fn __embassy_time_queue_item_from_waker`
12- Removed `TaskRef::dangling` and `TaskRef::timer_queue_item`
13- Added `embassy_time_queue_utils` as a dependency
14- Moved the `TimeQueueItem` struct and `timer-item-payload-size-*` features into embassy-time-queue-utils
15
11## 0.8.0 - 2025-07-31 16## 0.8.0 - 2025-07-31
12 17
13- Added `SpawnToken::id` 18- Added `SpawnToken::id`
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml
index 5e950bf45..bff13de56 100644
--- a/embassy-executor/Cargo.toml
+++ b/embassy-executor/Cargo.toml
@@ -35,6 +35,7 @@ rtos-trace = { version = "0.1.3", optional = true }
35 35
36embassy-executor-macros = { version = "0.7.0", path = "../embassy-executor-macros" } 36embassy-executor-macros = { version = "0.7.0", path = "../embassy-executor-macros" }
37embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true } 37embassy-time-driver = { version = "0.2", path = "../embassy-time-driver", optional = true }
38embassy-executor-timer-queue = { version = "0.1", path = "../embassy-executor-timer-queue" }
38critical-section = "1.1" 39critical-section = "1.1"
39 40
40document-features = "0.2.7" 41document-features = "0.2.7"
@@ -98,19 +99,3 @@ executor-interrupt = []
98trace = [] 99trace = []
99## Enable support for rtos-trace framework 100## Enable support for rtos-trace framework
100rtos-trace = ["dep:rtos-trace", "trace", "dep:embassy-time-driver"] 101rtos-trace = ["dep:rtos-trace", "trace", "dep:embassy-time-driver"]
101
102#! ### Timer Item Payload Size
103#! Sets the size of the payload for timer items, allowing integrated timer implementors to store
104#! additional data in the timer item. The payload field will be aligned to this value as well.
105#! If these features are not defined, the timer item will contain no payload field.
106
107_timer-item-payload = [] # A size was picked
108
109## 1 bytes
110timer-item-payload-size-1 = ["_timer-item-payload"]
111## 2 bytes
112timer-item-payload-size-2 = ["_timer-item-payload"]
113## 4 bytes
114timer-item-payload-size-4 = ["_timer-item-payload"]
115## 8 bytes
116timer-item-payload-size-8 = ["_timer-item-payload"]
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs
index c8f1f46c2..8e783b2af 100644
--- a/embassy-executor/src/raw/mod.rs
+++ b/embassy-executor/src/raw/mod.rs
@@ -16,7 +16,6 @@ mod run_queue;
16#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")] 16#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")]
17mod state; 17mod state;
18 18
19pub mod timer_queue;
20#[cfg(feature = "trace")] 19#[cfg(feature = "trace")]
21pub mod trace; 20pub mod trace;
22pub(crate) mod util; 21pub(crate) mod util;
@@ -31,8 +30,9 @@ use core::ptr::NonNull;
31#[cfg(not(feature = "arch-avr"))] 30#[cfg(not(feature = "arch-avr"))]
32use core::sync::atomic::AtomicPtr; 31use core::sync::atomic::AtomicPtr;
33use core::sync::atomic::Ordering; 32use core::sync::atomic::Ordering;
34use core::task::{Context, Poll}; 33use core::task::{Context, Poll, Waker};
35 34
35use embassy_executor_timer_queue::TimerQueueItem;
36#[cfg(feature = "arch-avr")] 36#[cfg(feature = "arch-avr")]
37use portable_atomic::AtomicPtr; 37use portable_atomic::AtomicPtr;
38 38
@@ -42,6 +42,11 @@ use self::util::{SyncUnsafeCell, UninitCell};
42pub use self::waker::task_from_waker; 42pub use self::waker::task_from_waker;
43use super::SpawnToken; 43use super::SpawnToken;
44 44
45#[no_mangle]
46extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem {
47 unsafe { task_from_waker(waker).timer_queue_item() }
48}
49
45/// Raw task header for use in task pointers. 50/// Raw task header for use in task pointers.
46/// 51///
47/// A task can be in one of the following states: 52/// A task can be in one of the following states:
@@ -88,7 +93,7 @@ pub(crate) struct TaskHeader {
88 poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>, 93 poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,
89 94
90 /// Integrated timer queue storage. This field should not be accessed outside of the timer queue. 95 /// Integrated timer queue storage. This field should not be accessed outside of the timer queue.
91 pub(crate) timer_queue_item: timer_queue::TimerQueueItem, 96 pub(crate) timer_queue_item: TimerQueueItem,
92 #[cfg(feature = "trace")] 97 #[cfg(feature = "trace")]
93 pub(crate) name: Option<&'static str>, 98 pub(crate) name: Option<&'static str>,
94 #[cfg(feature = "trace")] 99 #[cfg(feature = "trace")]
@@ -120,16 +125,6 @@ impl TaskRef {
120 } 125 }
121 } 126 }
122 127
123 /// # Safety
124 ///
125 /// The result of this function must only be compared
126 /// for equality, or stored, but not used.
127 pub const unsafe fn dangling() -> Self {
128 Self {
129 ptr: NonNull::dangling(),
130 }
131 }
132
133 pub(crate) fn header(self) -> &'static TaskHeader { 128 pub(crate) fn header(self) -> &'static TaskHeader {
134 unsafe { self.ptr.as_ref() } 129 unsafe { self.ptr.as_ref() }
135 } 130 }
@@ -140,9 +135,13 @@ impl TaskRef {
140 executor.as_ref().map(|e| Executor::wrap(e)) 135 executor.as_ref().map(|e| Executor::wrap(e))
141 } 136 }
142 137
143 /// Returns a reference to the timer queue item. 138 /// Returns a mutable reference to the timer queue item.
144 pub fn timer_queue_item(&self) -> &'static timer_queue::TimerQueueItem { 139 ///
145 &self.header().timer_queue_item 140 /// Safety
141 ///
142 /// This function must only be called in the context of the integrated timer queue.
143 unsafe fn timer_queue_item(mut self) -> &'static mut TimerQueueItem {
144 unsafe { &mut self.ptr.as_mut().timer_queue_item }
146 } 145 }
147 146
148 /// The returned pointer is valid for the entire TaskStorage. 147 /// The returned pointer is valid for the entire TaskStorage.
@@ -189,7 +188,7 @@ impl<F: Future + 'static> TaskStorage<F> {
189 // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` 188 // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss`
190 poll_fn: SyncUnsafeCell::new(None), 189 poll_fn: SyncUnsafeCell::new(None),
191 190
192 timer_queue_item: timer_queue::TimerQueueItem::new(), 191 timer_queue_item: TimerQueueItem::new(),
193 #[cfg(feature = "trace")] 192 #[cfg(feature = "trace")]
194 name: None, 193 name: None,
195 #[cfg(feature = "trace")] 194 #[cfg(feature = "trace")]
diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs
deleted file mode 100644
index e52453be4..000000000
--- a/embassy-executor/src/raw/timer_queue.rs
+++ /dev/null
@@ -1,73 +0,0 @@
1//! Timer queue operations.
2
3use core::cell::Cell;
4
5use super::TaskRef;
6
7#[cfg(feature = "_timer-item-payload")]
8macro_rules! define_opaque {
9 ($size:tt) => {
10 /// An opaque data type.
11 #[repr(align($size))]
12 pub struct OpaqueData {
13 data: [u8; $size],
14 }
15
16 impl OpaqueData {
17 const fn new() -> Self {
18 Self { data: [0; $size] }
19 }
20
21 /// Access the data as a reference to a type `T`.
22 ///
23 /// Safety:
24 ///
25 /// The caller must ensure that the size of the type `T` is less than, or equal to
26 /// the size of the payload, and must ensure that the alignment of the type `T` is
27 /// less than, or equal to the alignment of the payload.
28 ///
29 /// The type must be valid when zero-initialized.
30 pub unsafe fn as_ref<T>(&self) -> &T {
31 &*(self.data.as_ptr() as *const T)
32 }
33 }
34 };
35}
36
37#[cfg(feature = "timer-item-payload-size-1")]
38define_opaque!(1);
39#[cfg(feature = "timer-item-payload-size-2")]
40define_opaque!(2);
41#[cfg(feature = "timer-item-payload-size-4")]
42define_opaque!(4);
43#[cfg(feature = "timer-item-payload-size-8")]
44define_opaque!(8);
45
46/// An item in the timer queue.
47pub struct TimerQueueItem {
48 /// The next item in the queue.
49 ///
50 /// If this field contains `Some`, the item is in the queue. The last item in the queue has a
51 /// value of `Some(dangling_pointer)`
52 pub next: Cell<Option<TaskRef>>,
53
54 /// The time at which this item expires.
55 pub expires_at: Cell<u64>,
56
57 /// Some implementation-defined, zero-initialized piece of data.
58 #[cfg(feature = "_timer-item-payload")]
59 pub payload: OpaqueData,
60}
61
62unsafe impl Sync for TimerQueueItem {}
63
64impl TimerQueueItem {
65 pub(crate) const fn new() -> Self {
66 Self {
67 next: Cell::new(None),
68 expires_at: Cell::new(0),
69 #[cfg(feature = "_timer-item-payload")]
70 payload: OpaqueData::new(),
71 }
72 }
73}
diff --git a/embassy-time-queue-utils/CHANGELOG.md b/embassy-time-queue-utils/CHANGELOG.md
index ae4714f62..ebd6565d1 100644
--- a/embassy-time-queue-utils/CHANGELOG.md
+++ b/embassy-time-queue-utils/CHANGELOG.md
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7 7
8## Unreleased
9
10- Removed the embassy-executor dependency
11
12## 0.2.0 - 2025-08-04
13
14Bumpep embassy-executor
15
8## 0.1.0 - 2024-01-11 16## 0.1.0 - 2024-01-11
9 17
10Initial release 18Initial release
diff --git a/embassy-time-queue-utils/Cargo.toml b/embassy-time-queue-utils/Cargo.toml
index 93fa0ce3c..8991da66c 100644
--- a/embassy-time-queue-utils/Cargo.toml
+++ b/embassy-time-queue-utils/Cargo.toml
@@ -22,7 +22,7 @@ links = "embassy-time-queue"
22 22
23[dependencies] 23[dependencies]
24heapless = "0.8" 24heapless = "0.8"
25embassy-executor = { version = "0.8", path = "../embassy-executor" } 25embassy-executor-timer-queue = { version = "0.1", path = "../embassy-executor-timer-queue", features = ["timer-item-size-6-words"] }
26 26
27[features] 27[features]
28#! ### Generic Queue 28#! ### Generic Queue
diff --git a/embassy-time-queue-utils/src/lib.rs b/embassy-time-queue-utils/src/lib.rs
index 08e186432..a6f66913f 100644
--- a/embassy-time-queue-utils/src/lib.rs
+++ b/embassy-time-queue-utils/src/lib.rs
@@ -1,7 +1,6 @@
1#![no_std] 1#![no_std]
2#![doc = include_str!("../README.md")] 2#![doc = include_str!("../README.md")]
3#![warn(missing_docs)] 3#![warn(missing_docs)]
4#![deny(missing_debug_implementations)]
5 4
6#[cfg(feature = "_generic-queue")] 5#[cfg(feature = "_generic-queue")]
7pub mod queue_generic; 6pub mod queue_generic;
diff --git a/embassy-time-queue-utils/src/queue_integrated.rs b/embassy-time-queue-utils/src/queue_integrated.rs
index 748cd7843..2731d1ac6 100644
--- a/embassy-time-queue-utils/src/queue_integrated.rs
+++ b/embassy-time-queue-utils/src/queue_integrated.rs
@@ -1,16 +1,50 @@
1//! Timer queue operations. 1//! Timer queue operations.
2use core::cell::Cell; 2use core::cell::Cell;
3use core::cmp::min; 3use core::cmp::min;
4use core::ptr::NonNull;
4use core::task::Waker; 5use core::task::Waker;
5 6
6use embassy_executor::raw::TaskRef; 7use embassy_executor_timer_queue::TimerQueueItem;
8
9/// An item in the timer queue.
10#[derive(Default)]
11struct QueueItem {
12 /// The next item in the queue.
13 ///
14 /// If this field contains `Some`, the item is in the queue. The last item in the queue has a
15 /// value of `Some(dangling_pointer)`
16 pub next: Cell<Option<NonNull<QueueItem>>>,
17
18 /// The time at which this item expires.
19 pub expires_at: u64,
20
21 /// The registered waker. If Some, the item is enqueued in the timer queue.
22 pub waker: Option<Waker>,
23}
24
25unsafe impl Sync for QueueItem {}
7 26
8/// A timer queue, with items integrated into tasks. 27/// A timer queue, with items integrated into tasks.
9#[derive(Debug)] 28///
29/// # Safety
30///
31/// **This Queue is only safe when there is a single integrated queue in the system.**
32///
33/// If there are multiple integrated queues, additional checks are necessary to ensure that a Waker
34/// is not attempted to be enqueued in multiple queues.
10pub struct Queue { 35pub struct Queue {
11 head: Cell<Option<TaskRef>>, 36 head: Cell<Option<NonNull<QueueItem>>>,
12} 37}
13 38
39impl core::fmt::Debug for Queue {
40 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
41 f.debug_struct("Queue").finish()
42 }
43}
44
45unsafe impl Send for Queue {}
46unsafe impl Sync for Queue {}
47
14impl Queue { 48impl Queue {
15 /// Creates a new timer queue. 49 /// Creates a new timer queue.
16 pub const fn new() -> Self { 50 pub const fn new() -> Self {
@@ -22,25 +56,41 @@ impl Queue {
22 /// If this function returns `true`, the called should find the next expiration time and set 56 /// If this function returns `true`, the called should find the next expiration time and set
23 /// a new alarm for that time. 57 /// a new alarm for that time.
24 pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool { 58 pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool {
25 let task = embassy_executor::raw::task_from_waker(waker); 59 let item = unsafe {
26 let item = task.timer_queue_item(); 60 // Safety: the `&mut self`, along with the Safety note of the Queue, are sufficient to
27 if item.next.get().is_none() { 61 // ensure that this function creates the only mutable reference to the queue item.
28 // If not in the queue, add it and update. 62 TimerQueueItem::from_embassy_waker(waker)
29 let prev = self.head.replace(Some(task)); 63 };
30 item.next.set(if prev.is_none() { 64 let item = unsafe { item.as_mut::<QueueItem>() };
31 Some(unsafe { TaskRef::dangling() }) 65 match item.waker.as_ref() {
32 } else { 66 Some(_) if at <= item.expires_at => {
33 prev 67 // If expiration is sooner than previously set, update.
34 }); 68 item.expires_at = at;
35 item.expires_at.set(at); 69 // The waker is always stored in its own queue item, so we don't need to update it.
36 true 70
37 } else if at <= item.expires_at.get() { 71 // Trigger a queue update in case this item can be immediately dequeued.
38 // If expiration is sooner than previously set, update. 72 true
39 item.expires_at.set(at); 73 }
40 true 74 Some(_) => {
41 } else { 75 // Queue item does not need to be updated, the task will be scheduled to be woken
42 // Task does not need to be updated. 76 // before the new expiration.
43 false 77 false
78 }
79 None => {
80 // If not in the queue, add it and update.
81 let mut item_ptr = NonNull::from(item);
82 let prev = self.head.replace(Some(item_ptr));
83
84 let item = unsafe { item_ptr.as_mut() };
85
86 item.expires_at = at;
87 item.waker = Some(waker.clone());
88 item.next.set(prev);
89 // The default implementation doesn't care about the
90 // opaque payload, leave it unchanged.
91
92 true
93 }
44 } 94 }
45 } 95 }
46 96
@@ -51,33 +101,29 @@ impl Queue {
51 pub fn next_expiration(&mut self, now: u64) -> u64 { 101 pub fn next_expiration(&mut self, now: u64) -> u64 {
52 let mut next_expiration = u64::MAX; 102 let mut next_expiration = u64::MAX;
53 103
54 self.retain(|p| { 104 self.retain(|item| {
55 let item = p.timer_queue_item(); 105 if item.expires_at <= now {
56 let expires = item.expires_at.get();
57
58 if expires <= now {
59 // Timer expired, process task. 106 // Timer expired, process task.
60 embassy_executor::raw::wake_task(p); 107 if let Some(waker) = item.waker.take() {
108 waker.wake();
109 }
61 false 110 false
62 } else { 111 } else {
63 // Timer didn't yet expire, or never expires. 112 // Timer didn't yet expire, or never expires.
64 next_expiration = min(next_expiration, expires); 113 next_expiration = min(next_expiration, item.expires_at);
65 expires != u64::MAX 114 item.expires_at != u64::MAX
66 } 115 }
67 }); 116 });
68 117
69 next_expiration 118 next_expiration
70 } 119 }
71 120
72 fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { 121 fn retain(&mut self, mut f: impl FnMut(&mut QueueItem) -> bool) {
73 let mut prev = &self.head; 122 let mut prev = &self.head;
74 while let Some(p) = prev.get() { 123 while let Some(mut p) = prev.get() {
75 if unsafe { p == TaskRef::dangling() } { 124 let mut item = unsafe { p.as_mut() };
76 // prev was the last item, stop 125
77 break; 126 if f(&mut item) {
78 }
79 let item = p.timer_queue_item();
80 if f(p) {
81 // Skip to next 127 // Skip to next
82 prev = &item.next; 128 prev = &item.next;
83 } else { 129 } else {