Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
535c80e
Add initial DRS scheduler placeholder
jamesmunns Mar 20, 2025
1f50e4d
Implement Deadline Ranked Scheduling
jamesmunns Mar 20, 2025
8c70aaf
Make some things more consistent
jamesmunns Apr 1, 2025
ba0426f
Combine DRS and non-DRS atomic scheduler, using cordyceps
jamesmunns Apr 1, 2025
ed2e51b
Dependency enablement trickery
jamesmunns Apr 1, 2025
2a068c5
Conditional import
jamesmunns Apr 1, 2025
08a57b1
Update with changes from the PR
jamesmunns Apr 2, 2025
b65a3a3
Clean up some TODOs
jamesmunns Apr 3, 2025
b1b2955
Switch to released version of `cordyceps`, add error if used w/o atomics
jamesmunns Apr 8, 2025
3929142
One more must_use
jamesmunns Apr 8, 2025
0e28ba1
"Deadline Rank Sorted Scheduler" -> "Earliest Deadline First Scheduler"
jamesmunns Apr 16, 2025
0cb1ffe
Add EDF example
jamesmunns Apr 16, 2025
67ed473
rustfmt
jamesmunns Apr 16, 2025
7af8f35
There can be only one (run queue)
jamesmunns Jun 3, 2025
d88ea8d
Update with cordyceps changes
jamesmunns Jun 4, 2025
db06394
Inline the "MutexTransferStack" impl as it is unclear whether it will…
jamesmunns Jul 2, 2025
cf171ad
fmt
jamesmunns Jul 3, 2025
20b56b0
Update to use critical-section::Mutex instead of mutex::BlockingMutex
jamesmunns Jul 15, 2025
38e5e2e
Replace use of RefCell with UnsafeCell
jamesmunns Jul 15, 2025
4479f5b
Regular comments not doc comments
jamesmunns Jul 15, 2025
b5c9e72
Rename, remove excess mut
jamesmunns Jul 15, 2025
3f606b2
Change deadline to use internal atomics
diondokter Jul 15, 2025
d6d4df1
Add some docs
diondokter Jul 15, 2025
52d1785
Introduce metadata-deadline and let the EDF scheduler use it
diondokter Aug 29, 2025
99209ac
Add edf example to CI and fix the example
diondokter Aug 29, 2025
a853bbe
Happy CI :)
diondokter Aug 29, 2025
d04dc2b
Add cargo metadata
diondokter Aug 29, 2025
adb0c3e
Add more metadata
diondokter Sep 1, 2025
401fac6
Make requested API changes
diondokter Sep 8, 2025
09701a3
Fix example
diondokter Sep 8, 2025
2e21dcf
Fix metadata
diondokter Sep 8, 2025
e1209c5
executor: make Deadline actually private.
Dirbaio Sep 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ cargo batch \
--- build --release --manifest-path docs/examples/layer-by-layer/blinky-async/Cargo.toml --target thumbv7em-none-eabi \
--- build --release --manifest-path examples/nrf52810/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52810 \
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52840 \
--- build --release --manifest-path examples/nrf52840-edf/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/nrf52840-edf \
--- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf5340 \
--- build --release --manifest-path examples/nrf54l15/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf54l15 \
--- build --release --manifest-path examples/nrf9160/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/nrf9160 \
Expand Down
1 change: 1 addition & 0 deletions embassy-executor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added new metadata API for tasks.
- Main task automatically gets a name of `main` when the `metadata-name` feature is enabled.
- Upgraded rtos-trace
- Added optional "earliest deadline first" EDF scheduling

## 0.9.1 - 2025-08-31

Expand Down
20 changes: 17 additions & 3 deletions embassy-executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ build = [
{target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-thread"]},
{target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt"]},
{target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread"]},
{target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-deadline", "embassy-time-driver"]},
{target = "armv7a-none-eabi", features = ["arch-cortex-ar", "executor-thread"]},
{target = "armv7r-none-eabi", features = ["arch-cortex-ar", "executor-thread"]},
{target = "armv7r-none-eabihf", features = ["arch-cortex-ar", "executor-thread"]},
Expand All @@ -35,7 +36,7 @@ build = [
[package.metadata.embassy_docs]
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-executor-v$VERSION/embassy-executor/src/"
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-executor/src/"
features = ["defmt"]
features = ["defmt", "scheduler-deadline"]
flavors = [
{ name = "std", target = "x86_64-unknown-linux-gnu", features = ["arch-std", "executor-thread"] },
{ name = "wasm", target = "wasm32-unknown-unknown", features = ["arch-wasm", "executor-thread"] },
Expand All @@ -46,7 +47,7 @@ flavors = [
[package.metadata.docs.rs]
default-target = "thumbv7em-none-eabi"
targets = ["thumbv7em-none-eabi"]
features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"]
features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt", "scheduler-deadline", "embassy-time-driver"]

[dependencies]
defmt = { version = "1.0.1", optional = true }
Expand Down Expand Up @@ -76,6 +77,11 @@ js-sys = { version = "0.3", optional = true }
# arch-avr dependencies
avr-device = { version = "0.7.0", optional = true }


[dependencies.cordyceps]
version = "0.3.4"
features = ["no-cache-pad"]

[dev-dependencies]
critical-section = { version = "1.1", features = ["std"] }
trybuild = "1.0"
Expand Down Expand Up @@ -123,5 +129,13 @@ executor-interrupt = []
## Enable tracing hooks
trace = ["_any_trace"]
## Enable support for rtos-trace framework
rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "dep:embassy-time-driver"]
rtos-trace = ["_any_trace", "metadata-name", "dep:rtos-trace", "embassy-time-driver"]
_any_trace = []

## Enable "Earliest Deadline First" Scheduler, using soft-realtime "deadlines" to prioritize
## tasks based on the remaining time before their deadline. Adds some overhead.
scheduler-deadline = []

## Enable the embassy_time_driver dependency.
## This can unlock extra APIs, for example for the `sheduler-deadline`
embassy-time-driver = ["dep:embassy-time-driver"]
67 changes: 67 additions & 0 deletions embassy-executor/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,26 @@ use core::task::Poll;
use critical_section::Mutex;

use crate::raw;
#[cfg(feature = "scheduler-deadline")]
use crate::raw::Deadline;

/// Metadata associated with a task.
pub struct Metadata {
#[cfg(feature = "metadata-name")]
name: Mutex<Cell<Option<&'static str>>>,
#[cfg(feature = "scheduler-deadline")]
deadline: raw::Deadline,
}

impl Metadata {
pub(crate) const fn new() -> Self {
Self {
#[cfg(feature = "metadata-name")]
name: Mutex::new(Cell::new(None)),
// NOTE: The deadline is set to zero to allow the initializer to reside in `.bss`. This
// will be lazily initalized in `initialize_impl`
#[cfg(feature = "scheduler-deadline")]
deadline: raw::Deadline::new_unset(),
}
}

Expand Down Expand Up @@ -52,4 +60,63 @@ impl Metadata {
pub fn set_name(&self, name: &'static str) {
critical_section::with(|cs| self.name.borrow(cs).set(Some(name)))
}

/// Get this task's deadline.
#[cfg(feature = "scheduler-deadline")]
pub fn deadline(&self) -> u64 {
self.deadline.instant_ticks()
}

/// Set this task's deadline.
///
/// This method does NOT check whether the deadline has already passed.
#[cfg(feature = "scheduler-deadline")]
pub fn set_deadline(&self, instant_ticks: u64) {
self.deadline.set(instant_ticks);
}

/// Remove this task's deadline.
/// This brings it back to the defaul where it's not scheduled ahead of other tasks.
#[cfg(feature = "scheduler-deadline")]
pub fn unset_deadline(&self) {
self.deadline.set(Deadline::UNSET_TICKS);
}

/// Set this task's deadline `duration_ticks` in the future from when
/// this future is polled. This deadline is saturated to the max tick value.
///
/// Analogous to `Timer::after`.
#[cfg(all(feature = "scheduler-deadline", feature = "embassy-time-driver"))]
pub fn set_deadline_after(&self, duration_ticks: u64) {
let now = embassy_time_driver::now();

// Since ticks is a u64, saturating add is PROBABLY overly cautious, leave
// it for now, we can probably make this wrapping_add for performance
// reasons later.
let deadline = now.saturating_add(duration_ticks);

self.set_deadline(deadline);
}

/// Set the this task's deadline `increment_ticks` from the previous deadline.
///
/// This deadline is saturated to the max tick value.
///
/// Note that by default (unless otherwise set), tasks start life with the deadline
/// not set, which means this method will have no effect.
///
/// Analogous to one increment of `Ticker::every().next()`.
///
/// Returns the deadline that was set.
#[cfg(feature = "scheduler-deadline")]
pub fn increment_deadline(&self, duration_ticks: u64) {
let last = self.deadline();

// Since ticks is a u64, saturating add is PROBABLY overly cautious, leave
// it for now, we can probably make this wrapping_add for performance
// reasons later.
let deadline = last.saturating_add(duration_ticks);

self.set_deadline(deadline);
}
}
44 changes: 44 additions & 0 deletions embassy-executor/src/raw/deadline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use core::sync::atomic::{AtomicU32, Ordering};

/// A type for interacting with the deadline of the current task
///
/// Requires the `scheduler-deadline` feature.
///
/// Note: Interacting with the deadline should be done locally in a task.
/// In theory you could try to set or read the deadline from another task,
/// but that will result in weird (though not unsound) behavior.
pub(crate) struct Deadline {
instant_ticks_hi: AtomicU32,
instant_ticks_lo: AtomicU32,
}

impl Deadline {
pub(crate) const fn new(instant_ticks: u64) -> Self {
Self {
instant_ticks_hi: AtomicU32::new((instant_ticks >> 32) as u32),
instant_ticks_lo: AtomicU32::new(instant_ticks as u32),
}
}

pub(crate) const fn new_unset() -> Self {
Self::new(Self::UNSET_TICKS)
}

pub(crate) fn set(&self, instant_ticks: u64) {
self.instant_ticks_hi
.store((instant_ticks >> 32) as u32, Ordering::Relaxed);
self.instant_ticks_lo.store(instant_ticks as u32, Ordering::Relaxed);
}

/// Deadline value in ticks, same time base and ticks as `embassy-time`
pub(crate) fn instant_ticks(&self) -> u64 {
let hi = self.instant_ticks_hi.load(Ordering::Relaxed) as u64;
let lo = self.instant_ticks_lo.load(Ordering::Relaxed) as u64;

(hi << 32) | lo
}

/// Sentinel value representing an "unset" deadline, which has lower priority
/// than any other set deadline value
pub(crate) const UNSET_TICKS: u64 = u64::MAX;
}
13 changes: 11 additions & 2 deletions embassy-executor/src/raw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe
//! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_executor_macros::task) macro, which are fully safe.

#[cfg_attr(target_has_atomic = "ptr", path = "run_queue_atomics.rs")]
#[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")]
mod run_queue;

#[cfg_attr(all(cortex_m, target_has_atomic = "32"), path = "state_atomics_arm.rs")]
Expand All @@ -28,6 +26,9 @@ pub(crate) mod util;
#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")]
mod waker;

#[cfg(feature = "scheduler-deadline")]
mod deadline;

use core::future::Future;
use core::marker::PhantomData;
use core::mem;
Expand All @@ -38,6 +39,8 @@ use core::sync::atomic::AtomicPtr;
use core::sync::atomic::Ordering;
use core::task::{Context, Poll, Waker};

#[cfg(feature = "scheduler-deadline")]
pub(crate) use deadline::Deadline;
use embassy_executor_timer_queue::TimerQueueItem;
#[cfg(feature = "arch-avr")]
use portable_atomic::AtomicPtr;
Expand Down Expand Up @@ -96,6 +99,7 @@ extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static
pub(crate) struct TaskHeader {
pub(crate) state: State,
pub(crate) run_queue_item: RunQueueItem,

pub(crate) executor: AtomicPtr<SyncExecutor>,
poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,

Expand Down Expand Up @@ -296,6 +300,11 @@ impl<F: Future + 'static> AvailableTask<F> {
self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
self.task.future.write_in_place(future);

// By default, deadlines are set to the maximum value, so that any task WITH
// a set deadline will ALWAYS be scheduled BEFORE a task WITHOUT a set deadline
#[cfg(feature = "scheduler-deadline")]
self.task.raw.metadata.unset_deadline();

let task = TaskRef::new(self.task);

SpawnToken::new(task)
Expand Down
Loading