aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-executor/Cargo.toml18
-rw-r--r--embassy-executor/src/raw/deadline.rs2
-rw-r--r--embassy-executor/src/raw/mod.rs11
-rw-r--r--embassy-executor/src/raw/run_queue_atomics.rs19
4 files changed, 37 insertions, 13 deletions
diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml
index 80b5867c9..06e12ae7e 100644
--- a/embassy-executor/Cargo.toml
+++ b/embassy-executor/Cargo.toml
@@ -46,7 +46,7 @@ flavors = [
46[package.metadata.docs.rs] 46[package.metadata.docs.rs]
47default-target = "thumbv7em-none-eabi" 47default-target = "thumbv7em-none-eabi"
48targets = ["thumbv7em-none-eabi"] 48targets = ["thumbv7em-none-eabi"]
49features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"] 49features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "drs-scheduler"]
50 50
51[dependencies] 51[dependencies]
52defmt = { version = "1.0.1", optional = true } 52defmt = { version = "1.0.1", optional = true }
@@ -76,13 +76,17 @@ js-sys = { version = "0.3", optional = true }
76# arch-avr dependencies 76# arch-avr dependencies
77avr-device = { version = "0.7.0", optional = true } 77avr-device = { version = "0.7.0", optional = true }
78 78
79[dependencies.cordyceps] 79# Note: this is ONLY a dependency when the target has atomics, this is
80# note: targeting v0.3.3, to be released when 80# used for `run_queue_atomics`. We need to be conditional because
81# cordyceps *requires* the use of atomics, so we pull it in when
82# `run_queue_atomics` would be enabled, and NOT when `run_queue_critical_section`
83# would be enabled.
84[target.'cfg(target_has_atomic="ptr")'.dependencies.cordyceps]
85# TODO: targeting v0.3.3, to be released when
81# https://github.com/hawkw/mycelium/pull/520 is merged 86# https://github.com/hawkw/mycelium/pull/520 is merged
82version = "0.3" 87version = "0.3"
83git = "https://github.com/hawkw/mycelium" 88git = "https://github.com/hawkw/mycelium"
84rev = "9649db0525b9972b95937d83d52d3f51cc486281" 89rev = "9649db0525b9972b95937d83d52d3f51cc486281"
85optional = true
86 90
87[dev-dependencies] 91[dev-dependencies]
88critical-section = { version = "1.1", features = ["std"] } 92critical-section = { version = "1.1", features = ["std"] }
@@ -133,5 +137,7 @@ trace = ["_any_trace"]
133rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time-driver"] 137rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time-driver"]
134_any_trace = [] 138_any_trace = []
135 139
136## Enable "Deadline Rank Scheduler" 140## Enable "Deadline Rank Sorted" Scheduler, using soft-realtime "deadlines" to prioritize
137drs-scheduler = ["dep:cordyceps", "dep:embassy-time-driver"] 141## tasks based on the remaining time before their deadline. Adds some overhead. Requires
142## hardware atomic support
143drs-scheduler = ["dep:embassy-time-driver"]
diff --git a/embassy-executor/src/raw/deadline.rs b/embassy-executor/src/raw/deadline.rs
index 3f60936cc..c8cc94c52 100644
--- a/embassy-executor/src/raw/deadline.rs
+++ b/embassy-executor/src/raw/deadline.rs
@@ -2,6 +2,8 @@ use core::future::{poll_fn, Future};
2use core::task::Poll; 2use core::task::Poll;
3 3
4/// A type for interacting with the deadline of the current task 4/// A type for interacting with the deadline of the current task
5///
6/// Requires the `drs-scheduler` feature
5pub struct Deadline { 7pub struct Deadline {
6 /// Deadline value in ticks, same time base and ticks as `embassy-time` 8 /// Deadline value in ticks, same time base and ticks as `embassy-time`
7 pub instant_ticks: u64, 9 pub instant_ticks: u64,
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs
index 0dd247d30..f4fbe1bfc 100644
--- a/embassy-executor/src/raw/mod.rs
+++ b/embassy-executor/src/raw/mod.rs
@@ -101,14 +101,14 @@ extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static
101/// - 5: Task is dequeued. The task's future is not polled, because exiting the task replaces its `poll_fn`. 101/// - 5: Task is dequeued. The task's future is not polled, because exiting the task replaces its `poll_fn`.
102/// - 6: A task is waken when it is not spawned - `wake_task -> State::run_enqueue` 102/// - 6: A task is waken when it is not spawned - `wake_task -> State::run_enqueue`
103pub(crate) struct TaskHeader { 103pub(crate) struct TaskHeader {
104 pub(crate) state: State,
104 pub(crate) run_queue_item: RunQueueItem, 105 pub(crate) run_queue_item: RunQueueItem,
105 106
106 #[cfg(feature = "drs-scheduler")]
107 /// Deadline Rank Scheduler Deadline. This field should not be accessed outside the context of 107 /// Deadline Rank Scheduler Deadline. This field should not be accessed outside the context of
108 /// the task itself as it being polled by the executor. 108 /// the task itself as it being polled by the executor.
109 #[cfg(feature = "drs-scheduler")]
109 pub(crate) deadline: SyncUnsafeCell<u64>, 110 pub(crate) deadline: SyncUnsafeCell<u64>,
110 111
111 pub(crate) state: State,
112 pub(crate) executor: AtomicPtr<SyncExecutor>, 112 pub(crate) executor: AtomicPtr<SyncExecutor>,
113 poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>, 113 poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,
114 114
@@ -211,10 +211,12 @@ impl<F: Future + 'static> TaskStorage<F> {
211 pub const fn new() -> Self { 211 pub const fn new() -> Self {
212 Self { 212 Self {
213 raw: TaskHeader { 213 raw: TaskHeader {
214 state: State::new(),
214 run_queue_item: RunQueueItem::new(), 215 run_queue_item: RunQueueItem::new(),
216 // NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This
217 // will be lazily initalized in `initialize_impl`
215 #[cfg(feature = "drs-scheduler")] 218 #[cfg(feature = "drs-scheduler")]
216 deadline: SyncUnsafeCell::new(0u64), 219 deadline: SyncUnsafeCell::new(0u64),
217 state: State::new(),
218 executor: AtomicPtr::new(core::ptr::null_mut()), 220 executor: AtomicPtr::new(core::ptr::null_mut()),
219 // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` 221 // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss`
220 poll_fn: SyncUnsafeCell::new(None), 222 poll_fn: SyncUnsafeCell::new(None),
@@ -311,7 +313,8 @@ impl<F: Future + 'static> AvailableTask<F> {
311 self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll)); 313 self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
312 self.task.future.write_in_place(future); 314 self.task.future.write_in_place(future);
313 315
314 // TODO(AJM): Some other way of setting this? Just a placeholder 316 // By default, deadlines are set to the maximum value, so that any task WITH
317 // a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline
315 #[cfg(feature = "drs-scheduler")] 318 #[cfg(feature = "drs-scheduler")]
316 self.task.raw.deadline.set(u64::MAX); 319 self.task.raw.deadline.set(u64::MAX);
317 320
diff --git a/embassy-executor/src/raw/run_queue_atomics.rs b/embassy-executor/src/raw/run_queue_atomics.rs
index bc5d38250..3715fc658 100644
--- a/embassy-executor/src/raw/run_queue_atomics.rs
+++ b/embassy-executor/src/raw/run_queue_atomics.rs
@@ -66,6 +66,8 @@ impl RunQueue {
66 self.stack.push_was_empty(task) 66 self.stack.push_was_empty(task)
67 } 67 }
68 68
69 /// # Standard atomic runqueue
70 ///
69 /// Empty the queue, then call `on_task` for each task that was in the queue. 71 /// Empty the queue, then call `on_task` for each task that was in the queue.
70 /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue 72 /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue
71 /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. 73 /// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
@@ -78,9 +80,20 @@ impl RunQueue {
78 } 80 }
79 } 81 }
80 82
81 /// Empty the queue, then call `on_task` for each task that was in the queue. 83 /// # Deadline Ranked Sorted Scheduler
82 /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue 84 ///
83 /// and will be processed by the *next* call to `dequeue_all`, *not* the current one. 85 /// This algorithm will loop until all enqueued tasks are processed.
86 ///
87 /// Before polling a task, all currently enqueued tasks will be popped from the
88 /// runqueue, and will be added to the working `sorted` list, a linked-list that
89 /// sorts tasks by their deadline, with nearest deadline items in the front, and
90 /// furthest deadline items in the back.
91 ///
92 /// After popping and sorting all pending tasks, the SOONEST task will be popped
93 /// from the front of the queue, and polled by calling `on_task` on it.
94 ///
95 /// This process will repeat until the local `sorted` queue AND the global
96 /// runqueue are both empty, at which point this function will return.
84 #[cfg(feature = "drs-scheduler")] 97 #[cfg(feature = "drs-scheduler")]
85 pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) { 98 pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
86 // SAFETY: `deadline` can only be set through the `Deadline` interface, which 99 // SAFETY: `deadline` can only be set through the `Deadline` interface, which