diff options
| author | Dario Nieuwenhuis <[email protected]> | 2025-09-11 16:15:27 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2025-09-11 16:33:48 +0200 |
| commit | 6ec9bcb1c4dfbe5fc5365d93e75c516bb03bf9fc (patch) | |
| tree | 9094e4fb8745849ada00f61144aba60d8a51b5bf /embassy-executor | |
| parent | 42c68622eeba3be05e8f8ccdc4072b7aa57f78d1 (diff) | |
executor: add priority scheduler.
Diffstat (limited to 'embassy-executor')
| -rw-r--r-- | embassy-executor/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | embassy-executor/Cargo.toml | 14 | ||||
| -rw-r--r-- | embassy-executor/src/metadata.rs | 26 | ||||
| -rw-r--r-- | embassy-executor/src/raw/mod.rs | 5 | ||||
| -rw-r--r-- | embassy-executor/src/raw/run_queue.rs | 29 |
5 files changed, 62 insertions, 13 deletions
diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 03d60208e..6f079a11a 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md | |||
| @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 11 | - Added new metadata API for tasks. | 11 | - Added new metadata API for tasks. |
| 12 | - Main task automatically gets a name of `main` when the `metadata-name` feature is enabled. | 12 | - Main task automatically gets a name of `main` when the `metadata-name` feature is enabled. |
| 13 | - Upgraded rtos-trace | 13 | - Upgraded rtos-trace |
| 14 | - Added optional "highest priority" scheduling | ||
| 14 | - Added optional "earliest deadline first" EDF scheduling | 15 | - Added optional "earliest deadline first" EDF scheduling |
| 15 | 16 | ||
| 16 | ## 0.9.1 - 2025-08-31 | 17 | ## 0.9.1 - 2025-08-31 |
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 0ac666f80..d4ea0e6ac 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml | |||
| @@ -24,7 +24,12 @@ build = [ | |||
| 24 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread"]}, | 24 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread"]}, |
| 25 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt"]}, | 25 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt"]}, |
| 26 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"]}, | 26 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"]}, |
| 27 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-deadline", "embassy-time-driver"]}, | 27 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver"]}, |
| 28 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-priority"]}, | ||
| 29 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-priority", "scheduler-deadline"]}, | ||
| 30 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-deadline"]}, | ||
| 31 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-priority", "scheduler-deadline"]}, | ||
| 32 | {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-deadline"]}, | ||
| 28 | {target = "armv7a-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, | 33 | {target = "armv7a-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, |
| 29 | {target = "armv7r-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, | 34 | {target = "armv7r-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, |
| 30 | {target = "armv7r-none-eabihf", features = ["arch-cortex-ar", "executor-thread"]}, | 35 | {target = "armv7r-none-eabihf", features = ["arch-cortex-ar", "executor-thread"]}, |
| @@ -36,7 +41,7 @@ build = [ | |||
| 36 | [package.metadata.embassy_docs] | 41 | [package.metadata.embassy_docs] |
| 37 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" | 42 | src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" |
| 38 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" | 43 | src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" |
| 39 | features = ["defmt", "scheduler-deadline"] | 44 | features = ["defmt", "scheduler-deadline", "scheduler-priority"] |
| 40 | flavors = [ | 45 | flavors = [ |
| 41 | { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, | 46 | { name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] }, |
| 42 | { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, | 47 | { name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] }, |
| @@ -47,7 +52,7 @@ flavors = [ | |||
| 47 | [package.metadata.docs.rs] | 52 | [package.metadata.docs.rs] |
| 48 | default-target = "thumbv7em-none-eabi" | 53 | default-target = "thumbv7em-none-eabi" |
| 49 | targets = ["thumbv7em-none-eabi"] | 54 | targets = ["thumbv7em-none-eabi"] |
| 50 | features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "scheduler-deadline", "embassy-time-driver"] | 55 | features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "scheduler-deadline", "scheduler-priority", "embassy-time-driver"] |
| 51 | 56 | ||
| 52 | [dependencies] | 57 | [dependencies] |
| 53 | defmt = { version = "1.0.1", optional = true } | 58 | defmt = { version = "1.0.1", optional = true } |
| @@ -136,6 +141,9 @@ _any_trace = [] | |||
| 136 | ## tasks based on the remaining time before their deadline. Adds some overhead. | 141 | ## tasks based on the remaining time before their deadline. Adds some overhead. |
| 137 | scheduler-deadline = [] | 142 | scheduler-deadline = [] |
| 138 | 143 | ||
| 144 | ## Enable "Highest Priority First" Scheduler. Adds some overhead. | ||
| 145 | scheduler-priority = [] | ||
| 146 | |||
| 139 | ## Enable the embassy_time_driver dependency. | 147 | ## Enable the embassy_time_driver dependency. |
| 140 | ## This can unlock extra APIs, for example for the `sheduler-deadline` | 148 | ## This can unlock extra APIs, for example for the `sheduler-deadline` |
| 141 | embassy-time-driver = ["dep:embassy-time-driver"] | 149 | embassy-time-driver = ["dep:embassy-time-driver"] |
diff --git a/embassy-executor/src/metadata.rs b/embassy-executor/src/metadata.rs index 4220048a6..bc0df0f83 100644 --- a/embassy-executor/src/metadata.rs +++ b/embassy-executor/src/metadata.rs | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | #[cfg(feature = "metadata-name")] | 1 | #[cfg(feature = "metadata-name")] |
| 2 | use core::cell::Cell; | 2 | use core::cell::Cell; |
| 3 | use core::future::{poll_fn, Future}; | 3 | use core::future::{poll_fn, Future}; |
| 4 | #[cfg(feature = "scheduler-priority")] | ||
| 5 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 4 | use core::task::Poll; | 6 | use core::task::Poll; |
| 5 | 7 | ||
| 6 | #[cfg(feature = "metadata-name")] | 8 | #[cfg(feature = "metadata-name")] |
| @@ -14,6 +16,8 @@ use crate::raw::Deadline; | |||
| 14 | pub struct Metadata { | 16 | pub struct Metadata { |
| 15 | #[cfg(feature = "metadata-name")] | 17 | #[cfg(feature = "metadata-name")] |
| 16 | name: Mutex<Cell<Option<&'static str>>>, | 18 | name: Mutex<Cell<Option<&'static str>>>, |
| 19 | #[cfg(feature = "scheduler-priority")] | ||
| 20 | priority: AtomicU8, | ||
| 17 | #[cfg(feature = "scheduler-deadline")] | 21 | #[cfg(feature = "scheduler-deadline")] |
| 18 | deadline: raw::Deadline, | 22 | deadline: raw::Deadline, |
| 19 | } | 23 | } |
| @@ -23,6 +27,8 @@ impl Metadata { | |||
| 23 | Self { | 27 | Self { |
| 24 | #[cfg(feature = "metadata-name")] | 28 | #[cfg(feature = "metadata-name")] |
| 25 | name: Mutex::new(Cell::new(None)), | 29 | name: Mutex::new(Cell::new(None)), |
| 30 | #[cfg(feature = "scheduler-priority")] | ||
| 31 | priority: AtomicU8::new(0), | ||
| 26 | // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This | 32 | // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This |
| 27 | // will be lazily initalized in `initialize_impl` | 33 | // will be lazily initalized in `initialize_impl` |
| 28 | #[cfg(feature = "scheduler-deadline")] | 34 | #[cfg(feature = "scheduler-deadline")] |
| @@ -33,6 +39,14 @@ impl Metadata { | |||
| 33 | pub(crate) fn reset(&self) { | 39 | pub(crate) fn reset(&self) { |
| 34 | #[cfg(feature = "metadata-name")] | 40 | #[cfg(feature = "metadata-name")] |
| 35 | critical_section::with(|cs| self.name.borrow(cs).set(None)); | 41 | critical_section::with(|cs| self.name.borrow(cs).set(None)); |
| 42 | |||
| 43 | #[cfg(feature = "scheduler-priority")] | ||
| 44 | self.set_priority(0); | ||
| 45 | |||
| 46 | // By default, deadlines are set to the maximum value, so that any task WITH | ||
| 47 | // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline | ||
| 48 | #[cfg(feature = "scheduler-deadline")] | ||
| 49 | self.unset_deadline(); | ||
| 36 | } | 50 | } |
| 37 | 51 | ||
| 38 | /// Get the metadata for the current task. | 52 | /// Get the metadata for the current task. |
| @@ -61,6 +75,18 @@ impl Metadata { | |||
| 61 | critical_section::with(|cs| self.name.borrow(cs).set(Some(name))) | 75 | critical_section::with(|cs| self.name.borrow(cs).set(Some(name))) |
| 62 | } | 76 | } |
| 63 | 77 | ||
| 78 | /// Get this task's priority. | ||
| 79 | #[cfg(feature = "scheduler-priority")] | ||
| 80 | pub fn priority(&self) -> u8 { | ||
| 81 | self.priority.load(Ordering::Relaxed) | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Set this task's priority. | ||
| 85 | #[cfg(feature = "scheduler-priority")] | ||
| 86 | pub fn set_priority(&self, priority: u8) { | ||
| 87 | self.priority.store(priority, Ordering::Relaxed) | ||
| 88 | } | ||
| 89 | |||
| 64 | /// Get this task's deadline. | 90 | /// Get this task's deadline. |
| 65 | #[cfg(feature = "scheduler-deadline")] | 91 | #[cfg(feature = "scheduler-deadline")] |
| 66 | pub fn deadline(&self) -> u64 { | 92 | pub fn deadline(&self) -> u64 { |
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 51a363385..9f36c60bc 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs | |||
| @@ -300,11 +300,6 @@ impl<F: Future + 'static> AvailableTask<F> { | |||
| 300 | self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll)); | 300 | self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll)); |
| 301 | self.task.future.write_in_place(future); | 301 | self.task.future.write_in_place(future); |
| 302 | 302 | ||
| 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 | ||
| 305 | #[cfg(feature = "scheduler-deadline")] | ||
| 306 | self.task.raw.metadata.unset_deadline(); | ||
| 307 | |||
| 308 | let task = TaskRef::new(self.task); | 303 | let task = TaskRef::new(self.task); |
| 309 | 304 | ||
| 310 | SpawnToken::new(task) | 305 | SpawnToken::new(task) |
diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index d98c26f73..b8b052310 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 = "scheduler-deadline")] | 5 | #[cfg(any(feature = "scheduler-priority", 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 = "scheduler-deadline"))] | 86 | #[cfg(not(any(feature = "scheduler-priority", 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,10 +106,29 @@ 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 = "scheduler-deadline")] | 109 | #[cfg(any(feature = "scheduler-priority", 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 = | 111 | let mut sorted = SortedList::<TaskHeader>::new_with_cmp(|lhs, rhs| { |
| 112 | SortedList::<TaskHeader>::new_with_cmp(|lhs, rhs| lhs.metadata.deadline().cmp(&rhs.metadata.deadline())); | 112 | // compare by priority first |
| 113 | #[cfg(feature = "scheduler-priority")] | ||
| 114 | { | ||
| 115 | let lp = lhs.metadata.priority(); | ||
| 116 | let rp = rhs.metadata.priority(); | ||
| 117 | if lp != rp { | ||
| 118 | return lp.cmp(&rp).reverse(); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | // compare deadlines in case of tie. | ||
| 122 | #[cfg(feature = "scheduler-deadline")] | ||
| 123 | { | ||
| 124 | let ld = lhs.metadata.deadline(); | ||
| 125 | let rd = rhs.metadata.deadline(); | ||
| 126 | if ld != rd { | ||
| 127 | return ld.cmp(&rd); | ||
| 128 | } | ||
| 129 | } | ||
| 130 | core::cmp::Ordering::Equal | ||
| 131 | }); | ||
| 113 | 132 | ||
| 114 | loop { | 133 | loop { |
| 115 | // For each loop, grab any newly pended items | 134 | // For each loop, grab any newly pended items |
