diff options
| -rw-r--r-- | embassy-executor/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | embassy-executor/Cargo.toml | 16 | ||||
| -rw-r--r-- | embassy-executor/src/raw/mod.rs | 45 | ||||
| -rw-r--r-- | embassy-executor/src/raw/state_atomics.rs | 32 | ||||
| -rw-r--r-- | embassy-executor/src/raw/state_atomics_arm.rs | 36 | ||||
| -rw-r--r-- | embassy-executor/src/raw/state_critical_section.rs | 25 | ||||
| -rw-r--r-- | embassy-executor/src/raw/timer_queue.rs | 56 | ||||
| -rw-r--r-- | embassy-executor/tests/test.rs | 4 | ||||
| -rw-r--r-- | embassy-time-driver/src/lib.rs | 9 | ||||
| -rw-r--r-- | embassy-time-queue-driver/src/lib.rs | 28 | ||||
| -rw-r--r-- | embassy-time-queue-driver/src/queue_integrated.rs | 1 | ||||
| -rw-r--r-- | embassy-time/Cargo.toml | 8 | ||||
| -rw-r--r-- | embassy-time/src/timer.rs | 6 |
13 files changed, 80 insertions, 187 deletions
diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 068156210..59ea88d0c 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md | |||
| @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 10 | - embassy-executor no longer provides an `embassy-time-queue-driver` implementation | 10 | - embassy-executor no longer provides an `embassy-time-queue-driver` implementation |
| 11 | - Added `TaskRef::executor` to obtain a reference to a task's executor | 11 | - Added `TaskRef::executor` to obtain a reference to a task's executor |
| 12 | - integrated-timers are no longer processed when polling the executor. | 12 | - integrated-timers are no longer processed when polling the executor. |
| 13 | - Added the option to store data in timer queue items | ||
| 13 | 14 | ||
| 14 | ## 0.6.3 - 2024-11-12 | 15 | ## 0.6.3 - 2024-11-12 |
| 15 | 16 | ||
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 60fe7087a..2a64b9c83 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml | |||
| @@ -92,6 +92,22 @@ trace = [] | |||
| 92 | ## Enable support for rtos-trace framework | 92 | ## Enable support for rtos-trace framework |
| 93 | rtos-trace = ["dep:rtos-trace", "trace", "dep:embassy-time-driver"] | 93 | rtos-trace = ["dep:rtos-trace", "trace", "dep:embassy-time-driver"] |
| 94 | 94 | ||
| 95 | #! ### Timer Item Payload Size | ||
| 96 | #! Sets the size of the payload for timer items, allowing integrated timer implementors to store | ||
| 97 | #! additional data in the timer item. The payload field will be aligned to this value as well. | ||
| 98 | #! If these features are not defined, the timer item will contain no payload field. | ||
| 99 | |||
| 100 | _timer-item-payload = [] # A size was picked | ||
| 101 | |||
| 102 | ## 1 bytes | ||
| 103 | timer-item-payload-size-1 = ["_timer-item-payload"] | ||
| 104 | ## 2 bytes | ||
| 105 | timer-item-payload-size-2 = ["_timer-item-payload"] | ||
| 106 | ## 4 bytes | ||
| 107 | timer-item-payload-size-4 = ["_timer-item-payload"] | ||
| 108 | ## 8 bytes | ||
| 109 | timer-item-payload-size-8 = ["_timer-item-payload"] | ||
| 110 | |||
| 95 | #! ### Task Arena Size | 111 | #! ### Task Arena Size |
| 96 | #! Sets the [task arena](#task-arena) size. Necessary if you’re not using `nightly`. | 112 | #! Sets the [task arena](#task-arena) size. Necessary if you’re not using `nightly`. |
| 97 | #! | 113 | #! |
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 5a476213b..bdd5ff5ae 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs | |||
| @@ -96,29 +96,6 @@ impl TaskRef { | |||
| 96 | &self.header().timer_queue_item | 96 | &self.header().timer_queue_item |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | /// Mark the task as timer-queued. Return whether it should be actually enqueued | ||
| 100 | /// using `_embassy_time_schedule_wake`. | ||
| 101 | /// | ||
| 102 | /// Entering this state prevents the task from being respawned while in a timer queue. | ||
| 103 | /// | ||
| 104 | /// Safety: | ||
| 105 | /// | ||
| 106 | /// This functions should only be called by the timer queue driver, before | ||
| 107 | /// enqueueing the timer item. | ||
| 108 | pub unsafe fn timer_enqueue(&self) -> timer_queue::TimerEnqueueOperation { | ||
| 109 | self.header().state.timer_enqueue() | ||
| 110 | } | ||
| 111 | |||
| 112 | /// Unmark the task as timer-queued. | ||
| 113 | /// | ||
| 114 | /// Safety: | ||
| 115 | /// | ||
| 116 | /// This functions should only be called by the timer queue implementation, after the task has | ||
| 117 | /// been removed from the timer queue. | ||
| 118 | pub unsafe fn timer_dequeue(&self) { | ||
| 119 | self.header().state.timer_dequeue() | ||
| 120 | } | ||
| 121 | |||
| 122 | /// The returned pointer is valid for the entire TaskStorage. | 99 | /// The returned pointer is valid for the entire TaskStorage. |
| 123 | pub(crate) fn as_ptr(self) -> *const TaskHeader { | 100 | pub(crate) fn as_ptr(self) -> *const TaskHeader { |
| 124 | self.ptr.as_ptr() | 101 | self.ptr.as_ptr() |
| @@ -195,25 +172,7 @@ impl<F: Future + 'static> TaskStorage<F> { | |||
| 195 | match future.poll(&mut cx) { | 172 | match future.poll(&mut cx) { |
| 196 | Poll::Ready(_) => { | 173 | Poll::Ready(_) => { |
| 197 | this.future.drop_in_place(); | 174 | this.future.drop_in_place(); |
| 198 | |||
| 199 | // Mark this task to be timer queued. | ||
| 200 | // We're splitting the enqueue in two parts, so that we can change task state | ||
| 201 | // to something that prevent re-queueing. | ||
| 202 | let op = this.raw.state.timer_enqueue(); | ||
| 203 | |||
| 204 | // Now mark the task as not spawned, so that | ||
| 205 | // - it can be spawned again once it has been removed from the timer queue | ||
| 206 | // - it can not be timer-queued again | ||
| 207 | // We must do this before scheduling the wake, to prevent the task from being | ||
| 208 | // dequeued by the time driver while it's still SPAWNED. | ||
| 209 | this.raw.state.despawn(); | 175 | this.raw.state.despawn(); |
| 210 | |||
| 211 | // Now let's finish enqueueing. While we shouldn't get an `Ignore` here, it's | ||
| 212 | // better to be safe. | ||
| 213 | if op == timer_queue::TimerEnqueueOperation::Enqueue { | ||
| 214 | // Schedule the task in the past, so it gets dequeued ASAP. | ||
| 215 | unsafe { _embassy_time_schedule_wake(0, &waker) } | ||
| 216 | } | ||
| 217 | } | 176 | } |
| 218 | Poll::Pending => {} | 177 | Poll::Pending => {} |
| 219 | } | 178 | } |
| @@ -232,10 +191,6 @@ impl<F: Future + 'static> TaskStorage<F> { | |||
| 232 | } | 191 | } |
| 233 | } | 192 | } |
| 234 | 193 | ||
| 235 | extern "Rust" { | ||
| 236 | fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker); | ||
| 237 | } | ||
| 238 | |||
| 239 | /// An uninitialized [`TaskStorage`]. | 194 | /// An uninitialized [`TaskStorage`]. |
| 240 | pub struct AvailableTask<F: Future + 'static> { | 195 | pub struct AvailableTask<F: Future + 'static> { |
| 241 | task: &'static TaskStorage<F>, | 196 | task: &'static TaskStorage<F>, |
diff --git a/embassy-executor/src/raw/state_atomics.rs b/embassy-executor/src/raw/state_atomics.rs index d7350464f..6f5266bda 100644 --- a/embassy-executor/src/raw/state_atomics.rs +++ b/embassy-executor/src/raw/state_atomics.rs | |||
| @@ -1,7 +1,5 @@ | |||
| 1 | use core::sync::atomic::{AtomicU32, Ordering}; | 1 | use core::sync::atomic::{AtomicU32, Ordering}; |
| 2 | 2 | ||
| 3 | use super::timer_queue::TimerEnqueueOperation; | ||
| 4 | |||
| 5 | #[derive(Clone, Copy)] | 3 | #[derive(Clone, Copy)] |
| 6 | pub(crate) struct Token(()); | 4 | pub(crate) struct Token(()); |
| 7 | 5 | ||
| @@ -16,8 +14,6 @@ pub(crate) fn locked<R>(f: impl FnOnce(Token) -> R) -> R { | |||
| 16 | pub(crate) const STATE_SPAWNED: u32 = 1 << 0; | 14 | pub(crate) const STATE_SPAWNED: u32 = 1 << 0; |
| 17 | /// Task is in the executor run queue | 15 | /// Task is in the executor run queue |
| 18 | pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; | 16 | pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; |
| 19 | /// Task is in the executor timer queue | ||
| 20 | pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; | ||
| 21 | 17 | ||
| 22 | pub(crate) struct State { | 18 | pub(crate) struct State { |
| 23 | state: AtomicU32, | 19 | state: AtomicU32, |
| @@ -71,32 +67,4 @@ impl State { | |||
| 71 | let state = self.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); | 67 | let state = self.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); |
| 72 | state & STATE_SPAWNED != 0 | 68 | state & STATE_SPAWNED != 0 |
| 73 | } | 69 | } |
| 74 | |||
| 75 | /// Mark the task as timer-queued. Return whether it can be enqueued. | ||
| 76 | #[inline(always)] | ||
| 77 | pub fn timer_enqueue(&self) -> TimerEnqueueOperation { | ||
| 78 | if self | ||
| 79 | .state | ||
| 80 | .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { | ||
| 81 | // If not started, ignore it | ||
| 82 | if state & STATE_SPAWNED == 0 { | ||
| 83 | None | ||
| 84 | } else { | ||
| 85 | // Mark it as enqueued | ||
| 86 | Some(state | STATE_TIMER_QUEUED) | ||
| 87 | } | ||
| 88 | }) | ||
| 89 | .is_ok() | ||
| 90 | { | ||
| 91 | TimerEnqueueOperation::Enqueue | ||
| 92 | } else { | ||
| 93 | TimerEnqueueOperation::Ignore | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | /// Unmark the task as timer-queued. | ||
| 98 | #[inline(always)] | ||
| 99 | pub fn timer_dequeue(&self) { | ||
| 100 | self.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::Relaxed); | ||
| 101 | } | ||
| 102 | } | 70 | } |
diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs index c1e8f69ab..4896b33c5 100644 --- a/embassy-executor/src/raw/state_atomics_arm.rs +++ b/embassy-executor/src/raw/state_atomics_arm.rs | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | use core::arch::asm; | 1 | use core::arch::asm; |
| 2 | use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; | 2 | use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; |
| 3 | 3 | ||
| 4 | use super::timer_queue::TimerEnqueueOperation; | ||
| 5 | |||
| 6 | #[derive(Clone, Copy)] | 4 | #[derive(Clone, Copy)] |
| 7 | pub(crate) struct Token(()); | 5 | pub(crate) struct Token(()); |
| 8 | 6 | ||
| @@ -16,7 +14,6 @@ pub(crate) fn locked<R>(f: impl FnOnce(Token) -> R) -> R { | |||
| 16 | // Must be kept in sync with the layout of `State`! | 14 | // Must be kept in sync with the layout of `State`! |
| 17 | pub(crate) const STATE_SPAWNED: u32 = 1 << 0; | 15 | pub(crate) const STATE_SPAWNED: u32 = 1 << 0; |
| 18 | pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8; | 16 | pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8; |
| 19 | pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 16; | ||
| 20 | 17 | ||
| 21 | #[repr(C, align(4))] | 18 | #[repr(C, align(4))] |
| 22 | pub(crate) struct State { | 19 | pub(crate) struct State { |
| @@ -24,9 +21,8 @@ pub(crate) struct State { | |||
| 24 | spawned: AtomicBool, | 21 | spawned: AtomicBool, |
| 25 | /// Task is in the executor run queue | 22 | /// Task is in the executor run queue |
| 26 | run_queued: AtomicBool, | 23 | run_queued: AtomicBool, |
| 27 | /// Task is in the executor timer queue | ||
| 28 | timer_queued: AtomicBool, | ||
| 29 | pad: AtomicBool, | 24 | pad: AtomicBool, |
| 25 | pad2: AtomicBool, | ||
| 30 | } | 26 | } |
| 31 | 27 | ||
| 32 | impl State { | 28 | impl State { |
| @@ -34,8 +30,8 @@ impl State { | |||
| 34 | Self { | 30 | Self { |
| 35 | spawned: AtomicBool::new(false), | 31 | spawned: AtomicBool::new(false), |
| 36 | run_queued: AtomicBool::new(false), | 32 | run_queued: AtomicBool::new(false), |
| 37 | timer_queued: AtomicBool::new(false), | ||
| 38 | pad: AtomicBool::new(false), | 33 | pad: AtomicBool::new(false), |
| 34 | pad2: AtomicBool::new(false), | ||
| 39 | } | 35 | } |
| 40 | } | 36 | } |
| 41 | 37 | ||
| @@ -101,32 +97,4 @@ impl State { | |||
| 101 | self.run_queued.store(false, Ordering::Relaxed); | 97 | self.run_queued.store(false, Ordering::Relaxed); |
| 102 | r | 98 | r |
| 103 | } | 99 | } |
| 104 | |||
| 105 | /// Mark the task as timer-queued. Return whether it can be enqueued. | ||
| 106 | #[inline(always)] | ||
| 107 | pub fn timer_enqueue(&self) -> TimerEnqueueOperation { | ||
| 108 | if self | ||
| 109 | .as_u32() | ||
| 110 | .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { | ||
| 111 | // If not started, ignore it | ||
| 112 | if state & STATE_SPAWNED == 0 { | ||
| 113 | None | ||
| 114 | } else { | ||
| 115 | // Mark it as enqueued | ||
| 116 | Some(state | STATE_TIMER_QUEUED) | ||
| 117 | } | ||
| 118 | }) | ||
| 119 | .is_ok() | ||
| 120 | { | ||
| 121 | TimerEnqueueOperation::Enqueue | ||
| 122 | } else { | ||
| 123 | TimerEnqueueOperation::Ignore | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | /// Unmark the task as timer-queued. | ||
| 128 | #[inline(always)] | ||
| 129 | pub fn timer_dequeue(&self) { | ||
| 130 | self.timer_queued.store(false, Ordering::Relaxed); | ||
| 131 | } | ||
| 132 | } | 100 | } |
diff --git a/embassy-executor/src/raw/state_critical_section.rs b/embassy-executor/src/raw/state_critical_section.rs index 8e570b33c..29b10f6e3 100644 --- a/embassy-executor/src/raw/state_critical_section.rs +++ b/embassy-executor/src/raw/state_critical_section.rs | |||
| @@ -3,14 +3,10 @@ use core::cell::Cell; | |||
| 3 | pub(crate) use critical_section::{with as locked, CriticalSection as Token}; | 3 | pub(crate) use critical_section::{with as locked, CriticalSection as Token}; |
| 4 | use critical_section::{CriticalSection, Mutex}; | 4 | use critical_section::{CriticalSection, Mutex}; |
| 5 | 5 | ||
| 6 | use super::timer_queue::TimerEnqueueOperation; | ||
| 7 | |||
| 8 | /// Task is spawned (has a future) | 6 | /// Task is spawned (has a future) |
| 9 | pub(crate) const STATE_SPAWNED: u32 = 1 << 0; | 7 | pub(crate) const STATE_SPAWNED: u32 = 1 << 0; |
| 10 | /// Task is in the executor run queue | 8 | /// Task is in the executor run queue |
| 11 | pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; | 9 | pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; |
| 12 | /// Task is in the executor timer queue | ||
| 13 | pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; | ||
| 14 | 10 | ||
| 15 | pub(crate) struct State { | 11 | pub(crate) struct State { |
| 16 | state: Mutex<Cell<u32>>, | 12 | state: Mutex<Cell<u32>>, |
| @@ -81,25 +77,4 @@ impl State { | |||
| 81 | ok | 77 | ok |
| 82 | }) | 78 | }) |
| 83 | } | 79 | } |
| 84 | |||
| 85 | /// Mark the task as timer-queued. Return whether it can be enqueued. | ||
| 86 | #[inline(always)] | ||
| 87 | pub fn timer_enqueue(&self) -> TimerEnqueueOperation { | ||
| 88 | self.update(|s| { | ||
| 89 | // FIXME: we need to split SPAWNED into two phases, to prevent enqueueing a task that is | ||
| 90 | // just being spawned, because its executor pointer may still be changing. | ||
| 91 | if *s & STATE_SPAWNED == STATE_SPAWNED { | ||
| 92 | *s |= STATE_TIMER_QUEUED; | ||
| 93 | TimerEnqueueOperation::Enqueue | ||
| 94 | } else { | ||
| 95 | TimerEnqueueOperation::Ignore | ||
| 96 | } | ||
| 97 | }) | ||
| 98 | } | ||
| 99 | |||
| 100 | /// Unmark the task as timer-queued. | ||
| 101 | #[inline(always)] | ||
| 102 | pub fn timer_dequeue(&self) { | ||
| 103 | self.update(|s| *s &= !STATE_TIMER_QUEUED); | ||
| 104 | } | ||
| 105 | } | 80 | } |
diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 2ba0e00a9..e52453be4 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs | |||
| @@ -4,6 +4,45 @@ use core::cell::Cell; | |||
| 4 | 4 | ||
| 5 | use super::TaskRef; | 5 | use super::TaskRef; |
| 6 | 6 | ||
| 7 | #[cfg(feature = "_timer-item-payload")] | ||
| 8 | macro_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")] | ||
| 38 | define_opaque!(1); | ||
| 39 | #[cfg(feature = "timer-item-payload-size-2")] | ||
| 40 | define_opaque!(2); | ||
| 41 | #[cfg(feature = "timer-item-payload-size-4")] | ||
| 42 | define_opaque!(4); | ||
| 43 | #[cfg(feature = "timer-item-payload-size-8")] | ||
| 44 | define_opaque!(8); | ||
| 45 | |||
| 7 | /// An item in the timer queue. | 46 | /// An item in the timer queue. |
| 8 | pub struct TimerQueueItem { | 47 | pub struct TimerQueueItem { |
| 9 | /// The next item in the queue. | 48 | /// The next item in the queue. |
| @@ -14,6 +53,10 @@ pub struct TimerQueueItem { | |||
| 14 | 53 | ||
| 15 | /// The time at which this item expires. | 54 | /// The time at which this item expires. |
| 16 | pub expires_at: Cell<u64>, | 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, | ||
| 17 | } | 60 | } |
| 18 | 61 | ||
| 19 | unsafe impl Sync for TimerQueueItem {} | 62 | unsafe impl Sync for TimerQueueItem {} |
| @@ -23,17 +66,8 @@ impl TimerQueueItem { | |||
| 23 | Self { | 66 | Self { |
| 24 | next: Cell::new(None), | 67 | next: Cell::new(None), |
| 25 | expires_at: Cell::new(0), | 68 | expires_at: Cell::new(0), |
| 69 | #[cfg(feature = "_timer-item-payload")] | ||
| 70 | payload: OpaqueData::new(), | ||
| 26 | } | 71 | } |
| 27 | } | 72 | } |
| 28 | } | 73 | } |
| 29 | |||
| 30 | /// The operation to perform after `timer_enqueue` is called. | ||
| 31 | #[derive(Debug, Copy, Clone, PartialEq)] | ||
| 32 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 33 | #[must_use] | ||
| 34 | pub enum TimerEnqueueOperation { | ||
| 35 | /// Enqueue the task (or update its expiration time). | ||
| 36 | Enqueue, | ||
| 37 | /// The task must not be enqueued in the timer queue. | ||
| 38 | Ignore, | ||
| 39 | } | ||
diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 992ab3da9..0ce1f1891 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs | |||
| @@ -150,7 +150,3 @@ fn executor_task_cfg_args() { | |||
| 150 | let (_, _, _) = (a, b, c); | 150 | let (_, _, _) = (a, b, c); |
| 151 | } | 151 | } |
| 152 | } | 152 | } |
| 153 | |||
| 154 | // We need this for the test to compile, even though we don't want to use timers at the moment. | ||
| 155 | #[no_mangle] | ||
| 156 | fn _embassy_time_schedule_wake(_at: u64, _waker: &core::task::Waker) {} | ||
diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 57a9f7587..9f2795a01 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs | |||
| @@ -46,6 +46,9 @@ | |||
| 46 | //! | 46 | //! |
| 47 | //! Then, you'll need to adapt the `schedule_wake` method to use this queue. | 47 | //! Then, you'll need to adapt the `schedule_wake` method to use this queue. |
| 48 | //! | 48 | //! |
| 49 | //! Note that if you are using multiple queues, you will need to ensure that a single timer | ||
| 50 | //! queue item is only ever enqueued into a single queue at a time. | ||
| 51 | //! | ||
| 49 | //! ```ignore | 52 | //! ```ignore |
| 50 | //! use core::cell::RefCell; | 53 | //! use core::cell::RefCell; |
| 51 | //! use core::task::Waker; | 54 | //! use core::task::Waker; |
| @@ -131,6 +134,7 @@ pub trait Driver: Send + Sync + 'static { | |||
| 131 | 134 | ||
| 132 | extern "Rust" { | 135 | extern "Rust" { |
| 133 | fn _embassy_time_now() -> u64; | 136 | fn _embassy_time_now() -> u64; |
| 137 | fn _embassy_time_schedule_wake(at: u64, waker: &Waker); | ||
| 134 | } | 138 | } |
| 135 | 139 | ||
| 136 | /// See [`Driver::now`] | 140 | /// See [`Driver::now`] |
| @@ -138,6 +142,11 @@ pub fn now() -> u64 { | |||
| 138 | unsafe { _embassy_time_now() } | 142 | unsafe { _embassy_time_now() } |
| 139 | } | 143 | } |
| 140 | 144 | ||
| 145 | /// Schedule the given waker to be woken at `at`. | ||
| 146 | pub fn schedule_wake(at: u64, waker: &Waker) { | ||
| 147 | unsafe { _embassy_time_schedule_wake(at, waker) } | ||
| 148 | } | ||
| 149 | |||
| 141 | /// Set the time Driver implementation. | 150 | /// Set the time Driver implementation. |
| 142 | /// | 151 | /// |
| 143 | /// See the module documentation for an example. | 152 | /// See the module documentation for an example. |
diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs index 97c81a124..333b6124d 100644 --- a/embassy-time-queue-driver/src/lib.rs +++ b/embassy-time-queue-driver/src/lib.rs | |||
| @@ -10,8 +10,6 @@ | |||
| 10 | //! As a HAL implementer, you need to depend on this crate if you want to implement a time driver, | 10 | //! As a HAL implementer, you need to depend on this crate if you want to implement a time driver, |
| 11 | //! but how you should do so is documented in `embassy-time-driver`. | 11 | //! but how you should do so is documented in `embassy-time-driver`. |
| 12 | 12 | ||
| 13 | use core::task::Waker; | ||
| 14 | |||
| 15 | #[cfg(feature = "_generic-queue")] | 13 | #[cfg(feature = "_generic-queue")] |
| 16 | pub mod queue_generic; | 14 | pub mod queue_generic; |
| 17 | #[cfg(not(feature = "_generic-queue"))] | 15 | #[cfg(not(feature = "_generic-queue"))] |
| @@ -21,29 +19,3 @@ pub mod queue_integrated; | |||
| 21 | pub use queue_generic::Queue; | 19 | pub use queue_generic::Queue; |
| 22 | #[cfg(not(feature = "_generic-queue"))] | 20 | #[cfg(not(feature = "_generic-queue"))] |
| 23 | pub use queue_integrated::Queue; | 21 | pub use queue_integrated::Queue; |
| 24 | |||
| 25 | extern "Rust" { | ||
| 26 | fn _embassy_time_schedule_wake(at: u64, waker: &Waker); | ||
| 27 | } | ||
| 28 | |||
| 29 | /// Schedule the given waker to be woken at `at`. | ||
| 30 | pub fn schedule_wake(at: u64, waker: &Waker) { | ||
| 31 | // This function is not implemented in embassy-time-driver because it needs access to executor | ||
| 32 | // internals. The function updates task state, then delegates to the implementation provided | ||
| 33 | // by the time driver. | ||
| 34 | #[cfg(not(feature = "_generic-queue"))] | ||
| 35 | { | ||
| 36 | use embassy_executor::raw::task_from_waker; | ||
| 37 | use embassy_executor::raw::timer_queue::TimerEnqueueOperation; | ||
| 38 | // The very first thing we must do, before we even access the timer queue, is to | ||
| 39 | // mark the task a TIMER_QUEUED. This ensures that the task that is being scheduled | ||
| 40 | // can not be respawn while we are accessing the timer queue. | ||
| 41 | let task = task_from_waker(waker); | ||
| 42 | if unsafe { task.timer_enqueue() } == TimerEnqueueOperation::Ignore { | ||
| 43 | // We are not allowed to enqueue the task in the timer queue. This is because the | ||
| 44 | // task is not spawned, and so it makes no sense to schedule it. | ||
| 45 | return; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | unsafe { _embassy_time_schedule_wake(at, waker) } | ||
| 49 | } | ||
diff --git a/embassy-time-queue-driver/src/queue_integrated.rs b/embassy-time-queue-driver/src/queue_integrated.rs index 6bb4c0c1a..246cf1d63 100644 --- a/embassy-time-queue-driver/src/queue_integrated.rs +++ b/embassy-time-queue-driver/src/queue_integrated.rs | |||
| @@ -83,7 +83,6 @@ impl Queue { | |||
| 83 | // Remove it | 83 | // Remove it |
| 84 | prev.set(item.next.get()); | 84 | prev.set(item.next.get()); |
| 85 | item.next.set(None); | 85 | item.next.set(None); |
| 86 | unsafe { p.timer_dequeue() }; | ||
| 87 | } | 86 | } |
| 88 | } | 87 | } |
| 89 | } | 88 | } |
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index e3074119f..4f4ea0b14 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml | |||
| @@ -24,8 +24,8 @@ target = "x86_64-unknown-linux-gnu" | |||
| 24 | features = ["defmt", "std"] | 24 | features = ["defmt", "std"] |
| 25 | 25 | ||
| 26 | [features] | 26 | [features] |
| 27 | std = ["tick-hz-1_000_000", "critical-section/std"] | 27 | std = ["tick-hz-1_000_000", "critical-section/std", "dep:embassy-time-queue-driver"] |
| 28 | wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000"] | 28 | wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000", "dep:embassy-time-queue-driver"] |
| 29 | 29 | ||
| 30 | ## Display the time since startup next to defmt log messages. | 30 | ## Display the time since startup next to defmt log messages. |
| 31 | ## At most 1 `defmt-timestamp-uptime-*` feature can be used. | 31 | ## At most 1 `defmt-timestamp-uptime-*` feature can be used. |
| @@ -40,7 +40,7 @@ defmt-timestamp-uptime-tms = ["defmt"] | |||
| 40 | defmt-timestamp-uptime-tus = ["defmt"] | 40 | defmt-timestamp-uptime-tus = ["defmt"] |
| 41 | 41 | ||
| 42 | ## Create a `MockDriver` that can be manually advanced for testing purposes. | 42 | ## Create a `MockDriver` that can be manually advanced for testing purposes. |
| 43 | mock-driver = ["tick-hz-1_000_000"] | 43 | mock-driver = ["tick-hz-1_000_000", "dep:embassy-time-queue-driver"] |
| 44 | 44 | ||
| 45 | #! ### Tick Rate | 45 | #! ### Tick Rate |
| 46 | #! | 46 | #! |
| @@ -384,7 +384,7 @@ tick-hz-5_242_880_000 = ["embassy-time-driver/tick-hz-5_242_880_000"] | |||
| 384 | 384 | ||
| 385 | [dependencies] | 385 | [dependencies] |
| 386 | embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } | 386 | embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } |
| 387 | embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver" } | 387 | embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver", optional = true} |
| 388 | 388 | ||
| 389 | defmt = { version = "0.3", optional = true } | 389 | defmt = { version = "0.3", optional = true } |
| 390 | log = { version = "0.4.14", optional = true } | 390 | log = { version = "0.4.14", optional = true } |
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 4d7194b20..295ddbd9b 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs | |||
| @@ -157,7 +157,7 @@ impl Future for Timer { | |||
| 157 | if self.yielded_once && self.expires_at <= Instant::now() { | 157 | if self.yielded_once && self.expires_at <= Instant::now() { |
| 158 | Poll::Ready(()) | 158 | Poll::Ready(()) |
| 159 | } else { | 159 | } else { |
| 160 | embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); | 160 | embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); |
| 161 | self.yielded_once = true; | 161 | self.yielded_once = true; |
| 162 | Poll::Pending | 162 | Poll::Pending |
| 163 | } | 163 | } |
| @@ -238,7 +238,7 @@ impl Ticker { | |||
| 238 | self.expires_at += dur; | 238 | self.expires_at += dur; |
| 239 | Poll::Ready(()) | 239 | Poll::Ready(()) |
| 240 | } else { | 240 | } else { |
| 241 | embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); | 241 | embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); |
| 242 | Poll::Pending | 242 | Poll::Pending |
| 243 | } | 243 | } |
| 244 | }) | 244 | }) |
| @@ -255,7 +255,7 @@ impl Stream for Ticker { | |||
| 255 | self.expires_at += dur; | 255 | self.expires_at += dur; |
| 256 | Poll::Ready(Some(())) | 256 | Poll::Ready(Some(())) |
| 257 | } else { | 257 | } else { |
| 258 | embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); | 258 | embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); |
| 259 | Poll::Pending | 259 | Poll::Pending |
| 260 | } | 260 | } |
| 261 | } | 261 | } |
