diff options
Diffstat (limited to 'embassy-executor')
| -rw-r--r-- | embassy-executor/Cargo.toml | 10 | ||||
| -rw-r--r-- | embassy-executor/src/metadata.rs | 64 | ||||
| -rw-r--r-- | embassy-executor/src/raw/deadline.rs | 109 | ||||
| -rw-r--r-- | embassy-executor/src/raw/mod.rs | 6 | ||||
| -rw-r--r-- | embassy-executor/src/raw/run_queue.rs | 6 |
5 files changed, 71 insertions, 124 deletions
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index e7136466d..66352a00e 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml | |||
| @@ -119,8 +119,6 @@ arch-spin = ["_arch"] | |||
| 119 | 119 | ||
| 120 | ## Enable the `name` field in task metadata. | 120 | ## Enable the `name` field in task metadata. |
| 121 | metadata-name = ["embassy-executor-macros/metadata-name"] | 121 | metadata-name = ["embassy-executor-macros/metadata-name"] |
| 122 | ## Enable the `deadline` field in task metadata. | ||
| 123 | metadata-deadline = [] | ||
| 124 | 122 | ||
| 125 | #! ### Executor | 123 | #! ### Executor |
| 126 | 124 | ||
| @@ -131,9 +129,13 @@ executor-interrupt = [] | |||
| 131 | ## Enable tracing hooks | 129 | ## Enable tracing hooks |
| 132 | trace = ["_any_trace"] | 130 | trace = ["_any_trace"] |
| 133 | ## Enable support for rtos-trace framework | 131 | ## Enable support for rtos-trace framework |
| 134 | rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time-driver"] | 132 | rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "embassy-time-driver"] |
| 135 | _any_trace = [] | 133 | _any_trace = [] |
| 136 | 134 | ||
| 137 | ## Enable "Earliest Deadline First" Scheduler, using soft-realtime "deadlines" to prioritize | 135 | ## Enable "Earliest Deadline First" Scheduler, using soft-realtime "deadlines" to prioritize |
| 138 | ## tasks based on the remaining time before their deadline. Adds some overhead. | 136 | ## tasks based on the remaining time before their deadline. Adds some overhead. |
| 139 | edf-scheduler = ["dep:embassy-time-driver", "metadata-deadline"] | 137 | scheduler-deadline = [] |
| 138 | |||
| 139 | ## Enable the embassy_time_driver dependency. | ||
| 140 | ## This can unlock extra APIs, for example for the `sheduler-deadline` | ||
| 141 | embassy-time-driver = ["dep:embassy-time-driver"] | ||
diff --git a/embassy-executor/src/metadata.rs b/embassy-executor/src/metadata.rs index fd8095629..81c5afafb 100644 --- a/embassy-executor/src/metadata.rs +++ b/embassy-executor/src/metadata.rs | |||
| @@ -7,12 +7,14 @@ use core::task::Poll; | |||
| 7 | use critical_section::Mutex; | 7 | use critical_section::Mutex; |
| 8 | 8 | ||
| 9 | use crate::raw; | 9 | use crate::raw; |
| 10 | #[cfg(feature = "scheduler-deadline")] | ||
| 11 | use crate::raw::Deadline; | ||
| 10 | 12 | ||
| 11 | /// Metadata associated with a task. | 13 | /// Metadata associated with a task. |
| 12 | pub struct Metadata { | 14 | pub struct Metadata { |
| 13 | #[cfg(feature = "metadata-name")] | 15 | #[cfg(feature = "metadata-name")] |
| 14 | name: Mutex<Cell<Option<&'static str>>>, | 16 | name: Mutex<Cell<Option<&'static str>>>, |
| 15 | #[cfg(feature = "metadata-deadline")] | 17 | #[cfg(feature = "scheduler-deadline")] |
| 16 | deadline: raw::Deadline, | 18 | deadline: raw::Deadline, |
| 17 | } | 19 | } |
| 18 | 20 | ||
| @@ -23,7 +25,7 @@ impl Metadata { | |||
| 23 | name: Mutex::new(Cell::new(None)), | 25 | name: Mutex::new(Cell::new(None)), |
| 24 | // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This | 26 | // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This |
| 25 | // will be lazily initalized in `initialize_impl` | 27 | // will be lazily initalized in `initialize_impl` |
| 26 | #[cfg(feature = "metadata-deadline")] | 28 | #[cfg(feature = "scheduler-deadline")] |
| 27 | deadline: raw::Deadline::new_unset(), | 29 | deadline: raw::Deadline::new_unset(), |
| 28 | } | 30 | } |
| 29 | } | 31 | } |
| @@ -59,10 +61,62 @@ impl Metadata { | |||
| 59 | critical_section::with(|cs| self.name.borrow(cs).set(Some(name))) | 61 | critical_section::with(|cs| self.name.borrow(cs).set(Some(name))) |
| 60 | } | 62 | } |
| 61 | 63 | ||
| 62 | /// Earliest Deadline First scheduler Deadline. This field should not be accessed | 64 | /// Get this task's deadline. |
| 63 | /// outside the context of the task itself as it being polled by the executor. | 65 | #[cfg(feature = "scheduler-deadline")] |
| 64 | #[cfg(feature = "metadata-deadline")] | ||
| 65 | pub fn deadline(&self) -> &raw::Deadline { | 66 | pub fn deadline(&self) -> &raw::Deadline { |
| 66 | &self.deadline | 67 | &self.deadline |
| 67 | } | 68 | } |
| 69 | |||
| 70 | /// Set this task's deadline. | ||
| 71 | /// | ||
| 72 | /// This method does NOT check whether the deadline has already passed. | ||
| 73 | #[cfg(feature = "scheduler-deadline")] | ||
| 74 | pub fn set_deadline(&self, instant_ticks: u64) { | ||
| 75 | self.deadline.set(instant_ticks); | ||
| 76 | } | ||
| 77 | |||
| 78 | /// Remove this task's deadline. | ||
| 79 | /// This brings it back to the defaul where it's not scheduled ahead of other tasks. | ||
| 80 | #[cfg(feature = "scheduler-deadline")] | ||
| 81 | pub fn unset_deadline(&self) { | ||
| 82 | self.deadline.set(Deadline::UNSET_DEADLINE_TICKS); | ||
| 83 | } | ||
| 84 | |||
| 85 | /// Set this task's deadline `duration_ticks` in the future from when | ||
| 86 | /// this future is polled. This deadline is saturated to the max tick value. | ||
| 87 | /// | ||
| 88 | /// Analogous to `Timer::after`. | ||
| 89 | #[cfg(all(feature = "scheduler-deadline", feature = "embassy-time-driver"))] | ||
| 90 | pub fn set_deadline_after(&self, duration_ticks: u64) { | ||
| 91 | let now = embassy_time_driver::now(); | ||
| 92 | |||
| 93 | // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave | ||
| 94 | // it for now, we can probably make this wrapping_add for performance | ||
| 95 | // reasons later. | ||
| 96 | let deadline = now.saturating_add(duration_ticks); | ||
| 97 | |||
| 98 | self.set_deadline(deadline); | ||
| 99 | } | ||
| 100 | |||
| 101 | /// Set the this task's deadline `increment_ticks` from the previous deadline. | ||
| 102 | /// | ||
| 103 | /// This deadline is saturated to the max tick value. | ||
| 104 | /// | ||
| 105 | /// Note that by default (unless otherwise set), tasks start life with the deadline | ||
| 106 | /// not set, which means this method will have no effect. | ||
| 107 | /// | ||
| 108 | /// Analogous to one increment of `Ticker::every().next()`. | ||
| 109 | /// | ||
| 110 | /// Returns the deadline that was set. | ||
| 111 | #[cfg(feature = "scheduler-deadline")] | ||
| 112 | pub fn increment_deadline(&self, duration_ticks: u64) { | ||
| 113 | let last = self.deadline().instant_ticks(); | ||
| 114 | |||
| 115 | // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave | ||
| 116 | // it for now, we can probably make this wrapping_add for performance | ||
| 117 | // reasons later. | ||
| 118 | let deadline = last.saturating_add(duration_ticks); | ||
| 119 | |||
| 120 | self.set_deadline(deadline); | ||
| 121 | } | ||
| 68 | } | 122 | } |
diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs index 5b585195d..d08dd06ed 100644 --- a/embassy-executor/src/raw/deadline.rs +++ b/embassy-executor/src/raw/deadline.rs | |||
| @@ -1,6 +1,4 @@ | |||
| 1 | use core::future::{poll_fn, Future}; | ||
| 2 | use core::sync::atomic::{AtomicU32, Ordering}; | 1 | use core::sync::atomic::{AtomicU32, Ordering}; |
| 3 | use core::task::Poll; | ||
| 4 | 2 | ||
| 5 | /// A type for interacting with the deadline of the current task | 3 | /// A type for interacting with the deadline of the current task |
| 6 | /// | 4 | /// |
| @@ -49,111 +47,4 @@ impl Deadline { | |||
| 49 | pub fn is_unset(&self) -> bool { | 47 | pub fn is_unset(&self) -> bool { |
| 50 | self.instant_ticks() == Self::UNSET_DEADLINE_TICKS | 48 | self.instant_ticks() == Self::UNSET_DEADLINE_TICKS |
| 51 | } | 49 | } |
| 52 | |||
| 53 | /// Set the current task's deadline at exactly `instant_ticks` | ||
| 54 | /// | ||
| 55 | /// This method is a future in order to access the currently executing task's | ||
| 56 | /// header which contains the deadline. | ||
| 57 | /// | ||
| 58 | /// Analogous to `Timer::at`. | ||
| 59 | /// | ||
| 60 | /// This method does NOT check whether the deadline has already passed. | ||
| 61 | #[must_use = "Setting deadline must be polled to be effective"] | ||
| 62 | pub fn set_current_task_deadline(instant_ticks: u64) -> impl Future<Output = ()> { | ||
| 63 | poll_fn(move |cx| { | ||
| 64 | let task = super::task_from_waker(cx.waker()); | ||
| 65 | task.header().metadata.deadline().set(instant_ticks); | ||
| 66 | Poll::Ready(()) | ||
| 67 | }) | ||
| 68 | } | ||
| 69 | |||
| 70 | /// Set the current task's deadline `duration_ticks` in the future from when | ||
| 71 | /// this future is polled. This deadline is saturated to the max tick value. | ||
| 72 | /// | ||
| 73 | /// This method is a future in order to access the currently executing task's | ||
| 74 | /// header which contains the deadline. | ||
| 75 | /// | ||
| 76 | /// Analogous to `Timer::after`. | ||
| 77 | /// | ||
| 78 | /// Returns the deadline that was set. | ||
| 79 | #[must_use = "Setting deadline must be polled to be effective"] | ||
| 80 | pub fn set_current_task_deadline_after(duration_ticks: u64) -> impl Future<Output = Deadline> { | ||
| 81 | poll_fn(move |cx| { | ||
| 82 | let task = super::task_from_waker(cx.waker()); | ||
| 83 | let now = embassy_time_driver::now(); | ||
| 84 | |||
| 85 | // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave | ||
| 86 | // it for now, we can probably make this wrapping_add for performance | ||
| 87 | // reasons later. | ||
| 88 | let deadline = now.saturating_add(duration_ticks); | ||
| 89 | |||
| 90 | task.header().metadata.deadline().set(deadline); | ||
| 91 | |||
| 92 | Poll::Ready(Deadline::new(deadline)) | ||
| 93 | }) | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Set the current task's deadline `increment_ticks` from the previous deadline. | ||
| 97 | /// | ||
| 98 | /// This deadline is saturated to the max tick value. | ||
| 99 | /// | ||
| 100 | /// Note that by default (unless otherwise set), tasks start life with the deadline | ||
| 101 | /// u64::MAX, which means this method will have no effect. | ||
| 102 | /// | ||
| 103 | /// This method is a future in order to access the currently executing task's | ||
| 104 | /// header which contains the deadline | ||
| 105 | /// | ||
| 106 | /// Analogous to one increment of `Ticker::every().next()`. | ||
| 107 | /// | ||
| 108 | /// Returns the deadline that was set. | ||
| 109 | #[must_use = "Setting deadline must be polled to be effective"] | ||
| 110 | pub fn increment_current_task_deadline(increment_ticks: u64) -> impl Future<Output = Deadline> { | ||
| 111 | poll_fn(move |cx| { | ||
| 112 | let task_header = super::task_from_waker(cx.waker()).header(); | ||
| 113 | |||
| 114 | // Get the last value | ||
| 115 | let last = task_header.metadata.deadline().instant_ticks(); | ||
| 116 | |||
| 117 | // Since ticks is a u64, saturating add is PROBABLY overly cautious, leave | ||
| 118 | // it for now, we can probably make this wrapping_add for performance | ||
| 119 | // reasons later. | ||
| 120 | let deadline = last.saturating_add(increment_ticks); | ||
| 121 | |||
| 122 | // Store the new value | ||
| 123 | task_header.metadata.deadline().set(deadline); | ||
| 124 | |||
| 125 | Poll::Ready(Deadline::new(deadline)) | ||
| 126 | }) | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Get the current task's deadline as a tick value. | ||
| 130 | /// | ||
| 131 | /// This method is a future in order to access the currently executing task's | ||
| 132 | /// header which contains the deadline | ||
| 133 | pub fn get_current_task_deadline() -> impl Future<Output = Self> { | ||
| 134 | poll_fn(move |cx| { | ||
| 135 | let task = super::task_from_waker(cx.waker()); | ||
| 136 | |||
| 137 | let deadline = task.header().metadata.deadline().instant_ticks(); | ||
| 138 | Poll::Ready(Self::new(deadline)) | ||
| 139 | }) | ||
| 140 | } | ||
| 141 | |||
| 142 | /// Clear the current task's deadline, returning the previous value. | ||
| 143 | /// | ||
| 144 | /// This sets the deadline to the default value of `u64::MAX`, meaning all | ||
| 145 | /// tasks with set deadlines will be scheduled BEFORE this task. | ||
| 146 | #[must_use = "Clearing deadline must be polled to be effective"] | ||
| 147 | pub fn clear_current_task_deadline() -> impl Future<Output = Self> { | ||
| 148 | poll_fn(move |cx| { | ||
| 149 | let task_header = super::task_from_waker(cx.waker()).header(); | ||
| 150 | |||
| 151 | // get the old value | ||
| 152 | let deadline = task_header.metadata.deadline().instant_ticks(); | ||
| 153 | // Store the default value | ||
| 154 | task_header.metadata.deadline().set(Self::UNSET_DEADLINE_TICKS); | ||
| 155 | |||
| 156 | Poll::Ready(Self::new(deadline)) | ||
| 157 | }) | ||
| 158 | } | ||
| 159 | } | 50 | } |
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 86ee86842..6a9dd9749 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs | |||
| @@ -26,7 +26,7 @@ pub(crate) mod util; | |||
| 26 | #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] | 26 | #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] |
| 27 | mod waker; | 27 | mod waker; |
| 28 | 28 | ||
| 29 | #[cfg(feature = "metadata-deadline")] | 29 | #[cfg(feature = "scheduler-deadline")] |
| 30 | mod deadline; | 30 | mod deadline; |
| 31 | 31 | ||
| 32 | use core::future::Future; | 32 | use core::future::Future; |
| @@ -39,7 +39,7 @@ use core::sync::atomic::AtomicPtr; | |||
| 39 | use core::sync::atomic::Ordering; | 39 | use core::sync::atomic::Ordering; |
| 40 | use core::task::{Context, Poll, Waker}; | 40 | use core::task::{Context, Poll, Waker}; |
| 41 | 41 | ||
| 42 | #[cfg(feature = "metadata-deadline")] | 42 | #[cfg(feature = "scheduler-deadline")] |
| 43 | pub use deadline::Deadline; | 43 | pub use deadline::Deadline; |
| 44 | use embassy_executor_timer_queue::TimerQueueItem; | 44 | use embassy_executor_timer_queue::TimerQueueItem; |
| 45 | #[cfg(feature = "arch-avr")] | 45 | #[cfg(feature = "arch-avr")] |
| @@ -302,7 +302,7 @@ impl<F: Future + 'static> AvailableTask<F> { | |||
| 302 | 302 | ||
| 303 | // By default, deadlines are set to the maximum value, so that any task WITH | 303 | // By default, deadlines are set to the maximum value, so that any task WITH |
| 304 | // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline | 304 | // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline |
| 305 | #[cfg(feature = "edf-scheduler")] | 305 | #[cfg(feature = "scheduler-deadline")] |
| 306 | self.task | 306 | self.task |
| 307 | .raw | 307 | .raw |
| 308 | .metadata | 308 | .metadata |
diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index 978ca082a..29c977226 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs | |||
| @@ -2,7 +2,7 @@ use core::ptr::{addr_of_mut, NonNull}; | |||
| 2 | 2 | ||
| 3 | use cordyceps::sorted_list::Links; | 3 | use cordyceps::sorted_list::Links; |
| 4 | use cordyceps::Linked; | 4 | use cordyceps::Linked; |
| 5 | #[cfg(feature = "edf-scheduler")] | 5 | #[cfg(feature = "scheduler-deadline")] |
| 6 | use cordyceps::SortedList; | 6 | use cordyceps::SortedList; |
| 7 | 7 | ||
| 8 | #[cfg(target_has_atomic = "ptr")] | 8 | #[cfg(target_has_atomic = "ptr")] |
| @@ -83,7 +83,7 @@ impl RunQueue { | |||
| 83 | /// Empty the queue, then call `on_task` for each task that was in the queue. | 83 | /// Empty the queue, then call `on_task` for each task that was in the queue. |
| 84 | /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue | 84 | /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue |
| 85 | /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. | 85 | /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. |
| 86 | #[cfg(not(feature = "edf-scheduler"))] | 86 | #[cfg(not(feature = "scheduler-deadline"))] |
| 87 | pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { | 87 | pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { |
| 88 | let taken = self.stack.take_all(); | 88 | let taken = self.stack.take_all(); |
| 89 | for taskref in taken { | 89 | for taskref in taken { |
| @@ -106,7 +106,7 @@ impl RunQueue { | |||
| 106 | /// | 106 | /// |
| 107 | /// This process will repeat until the local `sorted` queue AND the global | 107 | /// This process will repeat until the local `sorted` queue AND the global |
| 108 | /// runqueue are both empty, at which point this function will return. | 108 | /// runqueue are both empty, at which point this function will return. |
| 109 | #[cfg(feature = "edf-scheduler")] | 109 | #[cfg(feature = "scheduler-deadline")] |
| 110 | pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { | 110 | pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { |
| 111 | let mut sorted = SortedList::<TaskHeader>::new_with_cmp(|lhs, rhs| { | 111 | let mut sorted = SortedList::<TaskHeader>::new_with_cmp(|lhs, rhs| { |
| 112 | lhs.metadata | 112 | lhs.metadata |
