aboutsummaryrefslogtreecommitdiff
path: root/embassy-time-queue-driver/src
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-time-queue-driver/src')
-rw-r--r--embassy-time-queue-driver/src/lib.rs140
-rw-r--r--embassy-time-queue-driver/src/queue_integrated.rs12
2 files changed, 17 insertions, 135 deletions
diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs
index 2d5fd449a..ed490a0ef 100644
--- a/embassy-time-queue-driver/src/lib.rs
+++ b/embassy-time-queue-driver/src/lib.rs
@@ -49,23 +49,18 @@
49//! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); 49//! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{});
50//! ``` 50//! ```
51 51
52use core::task::Waker;
53
52#[cfg(not(feature = "integrated-timers"))] 54#[cfg(not(feature = "integrated-timers"))]
53pub mod queue_generic; 55pub mod queue_generic;
54#[cfg(feature = "integrated-timers")] 56#[cfg(feature = "integrated-timers")]
55pub mod queue_integrated; 57pub mod queue_integrated;
56 58
57use core::cell::RefCell; 59#[cfg(feature = "integrated-timers")]
58use core::task::Waker; 60pub use queue_integrated::Queue;
59
60use critical_section::Mutex;
61 61
62/// Timer queue 62#[cfg(not(feature = "integrated-timers"))]
63pub trait TimerQueue { 63pub use queue_generic::Queue;
64 /// Schedules a waker in the queue to be awoken at moment `at`.
65 ///
66 /// If this moment is in the past, the waker might be awoken immediately.
67 fn schedule_wake(&'static self, at: u64, waker: &Waker);
68}
69 64
70extern "Rust" { 65extern "Rust" {
71 fn _embassy_time_schedule_wake(at: u64, waker: &Waker); 66 fn _embassy_time_schedule_wake(at: u64, waker: &Waker);
@@ -73,7 +68,10 @@ extern "Rust" {
73 68
74/// Schedule the given waker to be woken at `at`. 69/// Schedule the given waker to be woken at `at`.
75pub fn schedule_wake(at: u64, waker: &Waker) { 70pub fn schedule_wake(at: u64, waker: &Waker) {
76 #[cfg(feature = "integrated-timers")] 71 // This function is not implemented in embassy-time-driver because it needs access to executor
72 // internals. The function updates task state, then delegates to the implementation provided
73 // by the time driver.
74 #[cfg(not(feature = "_generic-queue"))]
77 { 75 {
78 use embassy_executor::raw::task_from_waker; 76 use embassy_executor::raw::task_from_waker;
79 use embassy_executor::raw::timer_queue::TimerEnqueueOperation; 77 use embassy_executor::raw::timer_queue::TimerEnqueueOperation;
@@ -89,121 +87,3 @@ pub fn schedule_wake(at: u64, waker: &Waker) {
89 } 87 }
90 unsafe { _embassy_time_schedule_wake(at, waker) } 88 unsafe { _embassy_time_schedule_wake(at, waker) }
91} 89}
92
93/// Set the TimerQueue implementation.
94///
95/// See the module documentation for an example.
96#[macro_export]
97macro_rules! timer_queue_impl {
98 (static $name:ident: $t: ty = $val:expr) => {
99 static $name: $t = $val;
100
101 #[no_mangle]
102 fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker) {
103 <$t as $crate::TimerQueue>::schedule_wake(&$name, at, waker);
104 }
105 };
106}
107
108#[cfg(feature = "integrated-timers")]
109type InnerQueue = queue_integrated::TimerQueue;
110
111#[cfg(not(feature = "integrated-timers"))]
112type InnerQueue = queue_generic::Queue;
113
114/// A timer queue implementation that can be used as a global timer queue.
115///
116/// This implementation is not thread-safe, and should be protected by a mutex of some sort.
117pub struct GenericTimerQueue<F: Fn(u64) -> bool> {
118 queue: InnerQueue,
119 set_alarm: F,
120}
121
122impl<F: Fn(u64) -> bool> GenericTimerQueue<F> {
123 /// Creates a new timer queue.
124 ///
125 /// `set_alarm` is a function that should set the next alarm time. The function should
126 /// return `true` if the alarm was set, and `false` if the alarm was in the past.
127 pub const fn new(set_alarm: F) -> Self {
128 Self {
129 queue: InnerQueue::new(),
130 set_alarm,
131 }
132 }
133
134 /// Schedules a task to run at a specific time, and returns whether any changes were made.
135 pub fn schedule_wake(&mut self, at: u64, waker: &core::task::Waker) {
136 #[cfg(feature = "integrated-timers")]
137 let waker = embassy_executor::raw::task_from_waker(waker);
138
139 if self.queue.schedule_wake(at, waker) {
140 self.dispatch()
141 }
142 }
143
144 /// Dequeues expired timers and returns the next alarm time.
145 pub fn next_expiration(&mut self, now: u64) -> u64 {
146 self.queue.next_expiration(now)
147 }
148
149 /// Handle the alarm.
150 ///
151 /// Call this function when the next alarm is due.
152 pub fn dispatch(&mut self) {
153 let mut next_expiration = self.next_expiration(embassy_time_driver::now());
154
155 while !(self.set_alarm)(next_expiration) {
156 // next_expiration is in the past, dequeue and find a new expiration
157 next_expiration = self.next_expiration(next_expiration);
158 }
159 }
160}
161
162/// A [`GenericTimerQueue`] protected by a critical section. Directly useable as a [`TimerQueue`].
163pub struct GlobalTimerQueue {
164 inner: Mutex<RefCell<GenericTimerQueue<fn(u64) -> bool>>>,
165}
166
167impl GlobalTimerQueue {
168 /// Creates a new timer queue.
169 ///
170 /// `set_alarm` is a function that should set the next alarm time. The function should
171 /// return `true` if the alarm was set, and `false` if the alarm was in the past.
172 pub const fn new(set_alarm: fn(u64) -> bool) -> Self {
173 Self {
174 inner: Mutex::new(RefCell::new(GenericTimerQueue::new(set_alarm))),
175 }
176 }
177
178 /// Schedules a task to run at a specific time, and returns whether any changes were made.
179 pub fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
180 critical_section::with(|cs| {
181 let mut inner = self.inner.borrow_ref_mut(cs);
182 inner.schedule_wake(at, waker);
183 });
184 }
185
186 /// Dequeues expired timers and returns the next alarm time.
187 pub fn next_expiration(&self, now: u64) -> u64 {
188 critical_section::with(|cs| {
189 let mut inner = self.inner.borrow_ref_mut(cs);
190 inner.next_expiration(now)
191 })
192 }
193
194 /// Handle the alarm.
195 ///
196 /// Call this function when the next alarm is due.
197 pub fn dispatch(&self) {
198 critical_section::with(|cs| {
199 let mut inner = self.inner.borrow_ref_mut(cs);
200 inner.dispatch()
201 })
202 }
203}
204
205impl TimerQueue for GlobalTimerQueue {
206 fn schedule_wake(&'static self, at: u64, waker: &Waker) {
207 GlobalTimerQueue::schedule_wake(self, at, waker)
208 }
209}
diff --git a/embassy-time-queue-driver/src/queue_integrated.rs b/embassy-time-queue-driver/src/queue_integrated.rs
index b905c00c3..6bb4c0c1a 100644
--- a/embassy-time-queue-driver/src/queue_integrated.rs
+++ b/embassy-time-queue-driver/src/queue_integrated.rs
@@ -1,15 +1,16 @@
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::task::Waker;
4 5
5use embassy_executor::raw::TaskRef; 6use embassy_executor::raw::TaskRef;
6 7
7/// A timer queue, with items integrated into tasks. 8/// A timer queue, with items integrated into tasks.
8pub struct TimerQueue { 9pub struct Queue {
9 head: Cell<Option<TaskRef>>, 10 head: Cell<Option<TaskRef>>,
10} 11}
11 12
12impl TimerQueue { 13impl Queue {
13 /// Creates a new timer queue. 14 /// Creates a new timer queue.
14 pub const fn new() -> Self { 15 pub const fn new() -> Self {
15 Self { head: Cell::new(None) } 16 Self { head: Cell::new(None) }
@@ -19,11 +20,12 @@ impl TimerQueue {
19 /// 20 ///
20 /// If this function returns `true`, the called should find the next expiration time and set 21 /// If this function returns `true`, the called should find the next expiration time and set
21 /// a new alarm for that time. 22 /// a new alarm for that time.
22 pub fn schedule_wake(&mut self, at: u64, p: TaskRef) -> bool { 23 pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool {
23 let item = p.timer_queue_item(); 24 let task = embassy_executor::raw::task_from_waker(waker);
25 let item = task.timer_queue_item();
24 if item.next.get().is_none() { 26 if item.next.get().is_none() {
25 // If not in the queue, add it and update. 27 // If not in the queue, add it and update.
26 let prev = self.head.replace(Some(p)); 28 let prev = self.head.replace(Some(task));
27 item.next.set(if prev.is_none() { 29 item.next.set(if prev.is_none() {
28 Some(unsafe { TaskRef::dangling() }) 30 Some(unsafe { TaskRef::dangling() })
29 } else { 31 } else {