Make requested API changes

This commit is contained in:
Dion Dokter 2025-09-08 11:40:34 +02:00 committed by Dario Nieuwenhuis
parent adb0c3e947
commit 401fac6ea9
5 changed files with 71 additions and 124 deletions

View File

@ -119,8 +119,6 @@ arch-spin = ["_arch"]
## Enable the `name` field in task metadata.
metadata-name = ["embassy-executor-macros/metadata-name"]
## Enable the `deadline` field in task metadata.
metadata-deadline = []
#! ### Executor
@ -131,9 +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.
edf-scheduler = ["dep:embassy-time-driver", "metadata-deadline"]
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"]

View File

@ -7,12 +7,14 @@ 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 = "metadata-deadline")]
#[cfg(feature = "scheduler-deadline")]
deadline: raw::Deadline,
}
@ -23,7 +25,7 @@ impl Metadata {
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 = "metadata-deadline")]
#[cfg(feature = "scheduler-deadline")]
deadline: raw::Deadline::new_unset(),
}
}
@ -59,10 +61,62 @@ impl Metadata {
critical_section::with(|cs| self.name.borrow(cs).set(Some(name)))
}
/// Earliest Deadline First scheduler Deadline. This field should not be accessed
/// outside the context of the task itself as it being polled by the executor.
#[cfg(feature = "metadata-deadline")]
/// Get this task's deadline.
#[cfg(feature = "scheduler-deadline")]
pub fn deadline(&self) -> &raw::Deadline {
&self.deadline
}
/// 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_DEADLINE_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().instant_ticks();
// 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);
}
}

View File

@ -1,6 +1,4 @@
use core::future::{poll_fn, Future};
use core::sync::atomic::{AtomicU32, Ordering};
use core::task::Poll;
/// A type for interacting with the deadline of the current task
///
@ -49,111 +47,4 @@ impl Deadline {
pub fn is_unset(&self) -> bool {
self.instant_ticks() == Self::UNSET_DEADLINE_TICKS
}
/// Set the current task's deadline at exactly `instant_ticks`
///
/// This method is a future in order to access the currently executing task's
/// header which contains the deadline.
///
/// Analogous to `Timer::at`.
///
/// This method does NOT check whether the deadline has already passed.
#[must_use = "Setting deadline must be polled to be effective"]
pub fn set_current_task_deadline(instant_ticks: u64) -> impl Future<Output = ()> {
poll_fn(move |cx| {
let task = super::task_from_waker(cx.waker());
task.header().metadata.deadline().set(instant_ticks);
Poll::Ready(())
})
}
/// Set the current task's deadline `duration_ticks` in the future from when
/// this future is polled. This deadline is saturated to the max tick value.
///
/// This method is a future in order to access the currently executing task's
/// header which contains the deadline.
///
/// Analogous to `Timer::after`.
///
/// Returns the deadline that was set.
#[must_use = "Setting deadline must be polled to be effective"]
pub fn set_current_task_deadline_after(duration_ticks: u64) -> impl Future<Output = Deadline> {
poll_fn(move |cx| {
let task = super::task_from_waker(cx.waker());
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);
task.header().metadata.deadline().set(deadline);
Poll::Ready(Deadline::new(deadline))
})
}
/// Set the current 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
/// u64::MAX, which means this method will have no effect.
///
/// This method is a future in order to access the currently executing task's
/// header which contains the deadline
///
/// Analogous to one increment of `Ticker::every().next()`.
///
/// Returns the deadline that was set.
#[must_use = "Setting deadline must be polled to be effective"]
pub fn increment_current_task_deadline(increment_ticks: u64) -> impl Future<Output = Deadline> {
poll_fn(move |cx| {
let task_header = super::task_from_waker(cx.waker()).header();
// Get the last value
let last = task_header.metadata.deadline().instant_ticks();
// 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(increment_ticks);
// Store the new value
task_header.metadata.deadline().set(deadline);
Poll::Ready(Deadline::new(deadline))
})
}
/// Get the current task's deadline as a tick value.
///
/// This method is a future in order to access the currently executing task's
/// header which contains the deadline
pub fn get_current_task_deadline() -> impl Future<Output = Self> {
poll_fn(move |cx| {
let task = super::task_from_waker(cx.waker());
let deadline = task.header().metadata.deadline().instant_ticks();
Poll::Ready(Self::new(deadline))
})
}
/// Clear the current task's deadline, returning the previous value.
///
/// This sets the deadline to the default value of `u64::MAX`, meaning all
/// tasks with set deadlines will be scheduled BEFORE this task.
#[must_use = "Clearing deadline must be polled to be effective"]
pub fn clear_current_task_deadline() -> impl Future<Output = Self> {
poll_fn(move |cx| {
let task_header = super::task_from_waker(cx.waker()).header();
// get the old value
let deadline = task_header.metadata.deadline().instant_ticks();
// Store the default value
task_header.metadata.deadline().set(Self::UNSET_DEADLINE_TICKS);
Poll::Ready(Self::new(deadline))
})
}
}

View File

@ -26,7 +26,7 @@ pub(crate) mod util;
#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")]
mod waker;
#[cfg(feature = "metadata-deadline")]
#[cfg(feature = "scheduler-deadline")]
mod deadline;
use core::future::Future;
@ -39,7 +39,7 @@ use core::sync::atomic::AtomicPtr;
use core::sync::atomic::Ordering;
use core::task::{Context, Poll, Waker};
#[cfg(feature = "metadata-deadline")]
#[cfg(feature = "scheduler-deadline")]
pub use deadline::Deadline;
use embassy_executor_timer_queue::TimerQueueItem;
#[cfg(feature = "arch-avr")]
@ -302,7 +302,7 @@ impl<F: Future + 'static> AvailableTask<F> {
// 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 = "edf-scheduler")]
#[cfg(feature = "scheduler-deadline")]
self.task
.raw
.metadata

View File

@ -2,7 +2,7 @@ use core::ptr::{addr_of_mut, NonNull};
use cordyceps::sorted_list::Links;
use cordyceps::Linked;
#[cfg(feature = "edf-scheduler")]
#[cfg(feature = "scheduler-deadline")]
use cordyceps::SortedList;
#[cfg(target_has_atomic = "ptr")]
@ -83,7 +83,7 @@ impl RunQueue {
/// Empty the queue, then call `on_task` for each task that was in the queue.
/// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue
/// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
#[cfg(not(feature = "edf-scheduler"))]
#[cfg(not(feature = "scheduler-deadline"))]
pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
let taken = self.stack.take_all();
for taskref in taken {
@ -106,7 +106,7 @@ impl RunQueue {
///
/// This process will repeat until the local `sorted` queue AND the global
/// runqueue are both empty, at which point this function will return.
#[cfg(feature = "edf-scheduler")]
#[cfg(feature = "scheduler-deadline")]
pub(crate) fn dequeue_all(&self, on_task: impl Fn(TaskRef)) {
let mut sorted = SortedList::<TaskHeader>::new_with_cmp(|lhs, rhs| {
lhs.metadata