diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-12-08 23:27:32 +0100 |
|---|---|---|
| committer | Dániel Buga <[email protected]> | 2024-12-13 21:20:59 +0100 |
| commit | b268b1795fed58544c166c41842ce0d66328aa3e (patch) | |
| tree | 55b6fb09f6694b5e3355d344770b36bfe1550415 /embassy-time-queue-driver/src | |
| parent | ec96395d084d5edc8be25ddaea8547e2ebd447a6 (diff) | |
Merge time-driver and time-queue-driver traits, make HALs own and handle the queue.
Diffstat (limited to 'embassy-time-queue-driver/src')
| -rw-r--r-- | embassy-time-queue-driver/src/lib.rs | 140 | ||||
| -rw-r--r-- | embassy-time-queue-driver/src/queue_integrated.rs | 12 |
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 | ||
| 52 | use core::task::Waker; | ||
| 53 | |||
| 52 | #[cfg(not(feature = "integrated-timers"))] | 54 | #[cfg(not(feature = "integrated-timers"))] |
| 53 | pub mod queue_generic; | 55 | pub mod queue_generic; |
| 54 | #[cfg(feature = "integrated-timers")] | 56 | #[cfg(feature = "integrated-timers")] |
| 55 | pub mod queue_integrated; | 57 | pub mod queue_integrated; |
| 56 | 58 | ||
| 57 | use core::cell::RefCell; | 59 | #[cfg(feature = "integrated-timers")] |
| 58 | use core::task::Waker; | 60 | pub use queue_integrated::Queue; |
| 59 | |||
| 60 | use critical_section::Mutex; | ||
| 61 | 61 | ||
| 62 | /// Timer queue | 62 | #[cfg(not(feature = "integrated-timers"))] |
| 63 | pub trait TimerQueue { | 63 | pub 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 | ||
| 70 | extern "Rust" { | 65 | extern "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`. |
| 75 | pub fn schedule_wake(at: u64, waker: &Waker) { | 70 | pub 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] | ||
| 97 | macro_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")] | ||
| 109 | type InnerQueue = queue_integrated::TimerQueue; | ||
| 110 | |||
| 111 | #[cfg(not(feature = "integrated-timers"))] | ||
| 112 | type 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. | ||
| 117 | pub struct GenericTimerQueue<F: Fn(u64) -> bool> { | ||
| 118 | queue: InnerQueue, | ||
| 119 | set_alarm: F, | ||
| 120 | } | ||
| 121 | |||
| 122 | impl<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`]. | ||
| 163 | pub struct GlobalTimerQueue { | ||
| 164 | inner: Mutex<RefCell<GenericTimerQueue<fn(u64) -> bool>>>, | ||
| 165 | } | ||
| 166 | |||
| 167 | impl 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 | |||
| 205 | impl 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. |
| 2 | use core::cell::Cell; | 2 | use core::cell::Cell; |
| 3 | use core::cmp::min; | 3 | use core::cmp::min; |
| 4 | use core::task::Waker; | ||
| 4 | 5 | ||
| 5 | use embassy_executor::raw::TaskRef; | 6 | use embassy_executor::raw::TaskRef; |
| 6 | 7 | ||
| 7 | /// A timer queue, with items integrated into tasks. | 8 | /// A timer queue, with items integrated into tasks. |
| 8 | pub struct TimerQueue { | 9 | pub struct Queue { |
| 9 | head: Cell<Option<TaskRef>>, | 10 | head: Cell<Option<TaskRef>>, |
| 10 | } | 11 | } |
| 11 | 12 | ||
| 12 | impl TimerQueue { | 13 | impl 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 { |
