aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2025-09-11 16:15:27 +0200
committerDario Nieuwenhuis <[email protected]>2025-09-11 16:33:48 +0200
commit6ec9bcb1c4dfbe5fc5365d93e75c516bb03bf9fc (patch)
tree9094e4fb8745849ada00f61144aba60d8a51b5bf
parent42c68622eeba3be05e8f8ccdc4072b7aa57f78d1 (diff)
executor: add priority scheduler.
-rwxr-xr-xci.sh6
-rw-r--r--embassy-executor/CHANGELOG.md1
-rw-r--r--embassy-executor/Cargo.toml14
-rw-r--r--embassy-executor/src/metadata.rs26
-rw-r--r--embassy-executor/src/raw/mod.rs5
-rw-r--r--embassy-executor/src/raw/run_queue.rs29
6 files changed, 68 insertions, 13 deletions
diff --git a/ci.sh b/ci.sh
index a25097690..81e665edf 100755
--- a/ci.sh
+++ b/ci.sh
@@ -38,6 +38,12 @@ cargo batch \
38 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \ 38 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \
39 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \ 39 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \
40 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt \ 40 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt \
41 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,embassy-time-driver \
42 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,embassy-time-driver,scheduler-priority \
43 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,embassy-time-driver,scheduler-priority,scheduler-deadline \
44 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,embassy-time-driver,scheduler-deadline \
45 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,scheduler-priority,scheduler-deadline \
46 --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,executor-thread,scheduler-deadline \
41 --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7a-none-eabi --features arch-cortex-ar,executor-thread \ 47 --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7a-none-eabi --features arch-cortex-ar,executor-thread \
42 --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabi --features arch-cortex-ar,executor-thread \ 48 --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabi --features arch-cortex-ar,executor-thread \
43 --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabihf --features arch-cortex-ar,executor-thread \ 49 --- build --release --manifest-path embassy-executor/Cargo.toml --target armv7r-none-eabihf --features arch-cortex-ar,executor-thread \
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]
37src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/" 42src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
38src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/" 43src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
39features = ["defmt", "scheduler-deadline"] 44features = ["defmt", "scheduler-deadline", "scheduler-priority"]
40flavors = [ 45flavors = [
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]
48default-target = "thumbv7em-none-eabi" 53default-target = "thumbv7em-none-eabi"
49targets = ["thumbv7em-none-eabi"] 54targets = ["thumbv7em-none-eabi"]
50features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "scheduler-deadline", "embassy-time-driver"] 55features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "scheduler-deadline", "scheduler-priority", "embassy-time-driver"]
51 56
52[dependencies] 57[dependencies]
53defmt = { version = "1.0.1", optional = true } 58defmt = { 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.
137scheduler-deadline = [] 142scheduler-deadline = []
138 143
144## Enable "Highest Priority First" Scheduler. Adds some overhead.
145scheduler-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`
141embassy-time-driver = ["dep:embassy-time-driver"] 149embassy-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")]
2use core::cell::Cell; 2use core::cell::Cell;
3use core::future::{poll_fn, Future}; 3use core::future::{poll_fn, Future};
4#[cfg(feature = "scheduler-priority")]
5use core::sync::atomic::{AtomicU8, Ordering};
4use core::task::Poll; 6use core::task::Poll;
5 7
6#[cfg(feature = "metadata-name")] 8#[cfg(feature = "metadata-name")]
@@ -14,6 +16,8 @@ use crate::raw::Deadline;
14pub struct Metadata { 16pub 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
3use cordyceps::sorted_list::Links; 3use cordyceps::sorted_list::Links;
4use cordyceps::Linked; 4use cordyceps::Linked;
5#[cfg(feature = "scheduler-deadline")] 5#[cfg(any(feature = "scheduler-priority", feature = "scheduler-deadline"))]
6use cordyceps::SortedList; 6use 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