diff options
Diffstat (limited to 'embassy-executor')
| -rw-r--r-- | embassy-executor/Cargo.toml | 18 | ||||
| -rw-r--r-- | embassy-executor/src/raw/deadline.rs | 2 | ||||
| -rw-r--r-- | embassy-executor/src/raw/mod.rs | 11 | ||||
| -rw-r--r-- | embassy-executor/src/raw/run_queue_atomics.rs | 19 |
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] |
| 47 | default-target = "thumbv7em-none-eabi" | 47 | default-target = "thumbv7em-none-eabi" |
| 48 | targets = ["thumbv7em-none-eabi"] | 48 | targets = ["thumbv7em-none-eabi"] |
| 49 | features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"] | 49 | features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "drs-scheduler"] |
| 50 | 50 | ||
| 51 | [dependencies] | 51 | [dependencies] |
| 52 | defmt = { version = "1.0.1", optional = true } | 52 | defmt = { 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 |
| 77 | avr-device = { version = "0.7.0", optional = true } | 77 | avr-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 |
| 82 | version = "0.3" | 87 | version = "0.3" |
| 83 | git = "https://github.com/hawkw/mycelium" | 88 | git = "https://github.com/hawkw/mycelium" |
| 84 | rev = "9649db0525b9972b95937d83d52d3f51cc486281" | 89 | rev = "9649db0525b9972b95937d83d52d3f51cc486281" |
| 85 | optional = true | ||
| 86 | 90 | ||
| 87 | [dev-dependencies] | 91 | [dev-dependencies] |
| 88 | critical-section = { version = "1.1", features = ["std"] } | 92 | critical-section = { version = "1.1", features = ["std"] } |
| @@ -133,5 +137,7 @@ trace = ["_any_trace"] | |||
| 133 | rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time-driver"] | 137 | rtos-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 |
| 137 | drs-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 | ||
| 143 | drs-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}; | |||
| 2 | use core::task::Poll; | 2 | use 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 | ||
| 5 | pub struct Deadline { | 7 | pub 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` |
| 103 | pub(crate) struct TaskHeader { | 103 | pub(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 |
