//! Tasks
use core::{fmt, hash, marker::PhantomData};
use raw::KernelBase;
use super::{
cfg, raw, raw_cfg, ActivateTaskError, Cfg, GetCurrentTaskError, GetTaskPriorityError,
InterruptTaskError, SetTaskPriorityError, UnparkError, UnparkExactError,
};
use crate::{
closure::{Closure, IntoClosureConst},
utils::{Init, PhantomInvariant},
};
// ----------------------------------------------------------------------------
define_object! {
/// Represents a single task in a system.
///
#[doc = common_doc_owned_handle!()]
///
/// <div class="admonition-follows"></div>
///
/// > **Relation to Other Specifications:** Present in almost every real-time
/// > operating system.
///
/// [`RawTaskId`]: raw::KernelBase::RawTaskId
///
/// # Task States
///
/// A task may be in one of the following states:
///
/// - **Dormant** — The task is not executing, doesn't have an associated
/// execution [thread], and can be [activated].
///
/// - **Ready** — The task has an associated execution thread, which is ready to
/// be scheduled to the CPU
///
/// - **Running** — The task has an associated execution thread, which is
/// currently scheduled to the CPU
///
/// - **Waiting** — The task has an associated execution thread, which is
/// currently blocked by a blocking operation
///
/// <center>
///
#[doc = svgbobdoc::transform!(
/// ```svgbob
/// .-------.
/// .--------------->| Ready |<--------------.
/// | '-------' |
/// | dispatch | ^ |
/// | | | |
/// | release | | | activate
/// .---------. | | .---------.
/// | Waiting | | | | Dormant |
/// '---------' | | '---------'
/// ^ | | ^
/// | | | |
/// | v | preempt |
/// | wait .---------. |
/// '---------------| Running |--------------'
/// '---------' exit
/// ```
)]
///
/// </center>
///
/// [thread]: crate#threads
/// [activated]: TaskMethods::activate
#[doc = include_str!("../common.md")]
pub struct Task<System: _>(System::RawTaskId);
/// Represents a single borrowed task in a system.
#[doc = include_str!("../common.md")]
pub struct TaskRef<System: raw::KernelBase>(_);
pub type StaticTask<System>;
pub trait TaskHandle {}
pub trait TaskMethods {}
}
impl<System: raw::KernelBase> StaticTask<System> {
/// Construct a `TaskDefiner` to define a task in [a configuration
/// function](crate#static-configuration).
pub const fn define() -> TaskDefiner<System> {
TaskDefiner::new()
}
}
/// A non-`Send`, `'static` [task] reference. The lack of `Send`-ness constrains
/// its lifetime to the owning task and thus allows it to represent a [current
/// task][1] [safely][2].
///
/// See [`TaskRef`][] for the `Send` counterpart.
/// **See [`TaskMethods`][] for the operations provided by this handle
/// type.**
///
/// [1]: Self::current
/// [2]: crate#object-safety
/// [task]: Task
/// [`TaskMethods`]: #impl-TaskMethods
#[doc = include_str!("../common.md")]
pub struct LocalTask<System: raw::KernelBase>(System::RawTaskId, PhantomData<*const ()>);
// Safety: `RawTaskId` is `Sync` by its definition
unsafe impl<System: raw::KernelBase> Sync for LocalTask<System> {}
// `impl Send for LocalTask` is left out intentionally.
unsafe impl<System: raw::KernelBase> const TaskHandle for LocalTask<System> {
type System = System;
#[inline]
unsafe fn from_id(id: System::RawTaskId) -> Self {
Self(id, PhantomData)
}
#[inline]
fn id(&self) -> System::RawTaskId {
self.0
}
#[inline]
fn borrow(&self) -> TaskRef<'_, Self::System> {
TaskRef(self.0, PhantomData)
}
}
impl<System: raw::KernelBase, T: TaskHandle<System = System>> PartialEq<T> for LocalTask<System> {
#[inline]
fn eq(&self, other: &T) -> bool {
self.0 == other.id()
}
}
impl<System: raw::KernelBase> Eq for LocalTask<System> {}
impl<System: raw::KernelBase> hash::Hash for LocalTask<System> {
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: hash::Hasher,
{
self.borrow().hash(state)
}
}
impl<System: raw::KernelBase> fmt::Debug for LocalTask<System> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.borrow().fmt(f)
}
}
impl<System: raw::KernelBase> Clone for LocalTask<System> {
#[inline]
fn clone(&self) -> Self {
Self(self.0, self.1)
}
}
impl<System: raw::KernelBase> Copy for LocalTask<System> {}
impl<System: raw::KernelBase> LocalTask<System> {
/// Get the current task (i.e., the task that is assigned to the current
/// processor and in the Running state).
///
/// Returns [`GetCurrentTaskError::BadContext`] if called from a non-task
/// context.
///
/// <div class="admonition-follows"></div>
///
/// > **Rationale:** Getting a current task in a non-task context does make
/// > sense, but the result may be soon invalidated (potentially violating
/// > the [object safety] of `LocalTask`) and made unreliable by various
/// > factors.
///
///
/// [1]: crate#object-safety
#[inline]
pub fn current() -> Result<Self, GetCurrentTaskError> {
// Safety: Constructing a `LocalTask` for a current task is okay.
// Also, `LocalTask` cannot outlive the current thread because
// it's `!Send`, and consequently it cannot outlive the
// represented task.
System::raw_task_current().map(|id| unsafe { Self::from_id(id) })
}
}
/// The supported operations on [`TaskHandle`].
#[doc = include_str!("../common.md")]
pub trait TaskMethods: TaskHandle {
/// Start the execution of the task.
#[inline]
fn activate(&self) -> Result<(), ActivateTaskError> {
// Safety: `Task` represents a permission to access the
// referenced object.
unsafe { <Self::System as KernelBase>::raw_task_activate(self.id()) }
}
/// Interrupt any ongoing wait operations undertaken by the task.
///
/// This method interrupt any ongoing system call that is blocking the task.
/// The interrupted system call will return [`WaitError::Interrupted`] or
/// [`WaitTimeoutError::Interrupted`].
///
/// [`WaitError::Interrupted`]: crate::kernel::WaitError::Interrupted
/// [`WaitTimeoutError::Interrupted`]: crate::kernel::WaitTimeoutError::Interrupted
#[inline]
fn interrupt(&self) -> Result<(), InterruptTaskError> {
// Safety: `Task` represents a permission to access the
// referenced object.
unsafe { <Self::System as KernelBase>::raw_task_interrupt(self.id()) }
}
/// Make the task's token available, unblocking [`Kernel::park`][] now or in
/// the future.
///
/// If the token is already available, this method will return without doing
/// anything. Use [`Self::unpark_exact`] if you need to detect this
/// condition.
///
/// If the task is currently being blocked by `Kernel::park`, the token will
/// be immediately consumed. Otherwise, it will be consumed on a next call
/// to `Kernel::park`.
///
/// [`Kernel::park`]: crate::kernel::Kernel::park
#[inline]
fn unpark(&self) -> Result<(), UnparkError> {
match self.unpark_exact() {
Ok(()) | Err(UnparkExactError::QueueOverflow) => Ok(()),
Err(UnparkExactError::BadContext) => Err(UnparkError::BadContext),
Err(UnparkExactError::NoAccess) => Err(UnparkError::NoAccess),
Err(UnparkExactError::BadObjectState) => Err(UnparkError::BadObjectState),
}
}
/// Make *exactly* one new token available for the task, unblocking
/// [`Kernel::park`] now or in the future.
///
/// If the token is already available, this method will return
/// [`UnparkExactError::QueueOverflow`]. Thus, this method will succeed
/// only if it made *exactly* one token available.
///
/// If the task is currently being blocked by `Kernel::park`, the token will
/// be immediately consumed. Otherwise, it will be consumed on a next call
/// to `Kernel::park`.
///
/// [`Kernel::park`]: crate::kernel::Kernel::park
#[inline]
fn unpark_exact(&self) -> Result<(), UnparkExactError> {
// Safety: `Task` represents a permission to access the
// referenced object.
unsafe { <Self::System as KernelBase>::raw_task_unpark_exact(self.id()) }
}
/// Set the task's base priority.
///
/// A task's base priority is used to calculate its [effective priority].
/// Tasks with lower effective priorities execute first. The base priority
/// is reset to the initial value specified by [`TaskDefiner::priority`]
/// upon activation.
///
/// [effective priority]: Self::effective_priority
/// [`TaskDefiner::priority`]: crate::kernel::task::TaskDefiner::priority
///
/// The value must be in range `0..`[`num_task_priority_levels`]. Otherwise,
/// this method will return [`SetTaskPriorityError::BadParam`].
///
/// The task shouldn't be in the Dormant state. Otherwise, this method will
/// return [`SetTaskPriorityError::BadObjectState`].
///
/// [`num_task_priority_levels`]: crate::kernel::Cfg::num_task_priority_levels
#[inline]
fn set_priority(&self, priority: usize) -> Result<(), SetTaskPriorityError>
where
Self::System: raw::KernelTaskSetPriority,
{
// Safety: `Task` represents a permission to access the
// referenced object.
unsafe {
<Self::System as raw::KernelTaskSetPriority>::raw_task_set_priority(self.id(), priority)
}
}
/// Get the task's base priority.
///
/// The task shouldn't be in the Dormant state. Otherwise, this method will
/// return [`GetTaskPriorityError::BadObjectState`].
#[inline]
fn priority(&self) -> Result<usize, GetTaskPriorityError> {
// Safety: `Task` represents a permission to access the
// referenced object.
unsafe { <Self::System as raw::KernelBase>::raw_task_priority(self.id()) }
}
/// Get the task's effective priority.
///
/// The effective priority is calculated based on the task's [base priority]
/// and can be temporarily raised by a [mutex locking protocol].
///
/// [base priority]: Self::priority
/// [mutex locking protocol]: crate::kernel::MutexProtocol
///
/// The task shouldn't be in the Dormant state. Otherwise, this method will
/// return [`GetTaskPriorityError::BadObjectState`].
#[inline]
fn effective_priority(&self) -> Result<usize, GetTaskPriorityError> {
// Safety: `Task` represents a permission to access the
// referenced object.
unsafe { <Self::System as raw::KernelBase>::raw_task_effective_priority(self.id()) }
}
}
impl<T: TaskHandle> TaskMethods for T {}
// ----------------------------------------------------------------------------
/// The definer (static builder) for [`TaskRef`].
#[must_use = "must call `finish()` to complete registration"]
pub struct TaskDefiner<System> {
_phantom: PhantomInvariant<System>,
start: Option<Closure>,
stack_size: Option<usize>,
priority: Option<usize>,
active: bool,
}
impl<System: raw::KernelBase> TaskDefiner<System> {
const fn new() -> Self {
Self {
_phantom: Init::INIT,
start: None,
stack_size: None,
priority: None,
active: false,
}
}
/// \[**Required**\] Specify the task's entry point.
pub const fn start<C: ~const IntoClosureConst>(self, start: C) -> Self {
Self {
start: Some(start.into_closure_const()),
..self
}
}
/// Specify the task's stack size.
pub const fn stack_size(self, stack_size: usize) -> Self {
assert!(
self.stack_size.is_none(),
"the task's stack is already specified"
);
Self {
stack_size: Some(stack_size),
..self
}
}
// TODO: custom stack storage
/// \[**Required**\] Specify the task's initial base priority. Tasks with
/// lower priority values execute first. The value must be in range
/// `0..`[`num_task_priority_levels`].
///
/// [`num_task_priority_levels`]: crate::kernel::Cfg::num_task_priority_levels
pub const fn priority(self, priority: usize) -> Self {
Self {
priority: Some(priority),
..self
}
}
/// Specify whether the task should be activated at system startup.
/// Defaults to `false` (don't activate).
pub const fn active(self, active: bool) -> Self {
Self { active, ..self }
}
/// Complete the definition of a task, returning a reference to the
/// task.
pub const fn finish<C: ~const raw_cfg::CfgTask<System = System>>(
self,
cfg: &mut Cfg<C>,
) -> StaticTask<System> {
let id = cfg.raw().task_define(
raw_cfg::TaskDescriptor {
phantom: Init::INIT,
start: self
.start
.expect("`start` (task entry point) is not specified"),
active: self.active,
priority: self
.priority
.expect("`priority` (task entry point) is not specified"),
stack_size: self.stack_size,
},
(),
);
unsafe { TaskRef::from_id(id) }
}
}
/// Specifies the [`Hunk`] to use as a task's stack when included in the task's
/// property [`Bag`].
///
/// A kernel might choose to ignore this if `StackHunk` is not supported.
///
/// If a `StackHunk` is given, the stack size ([`TaskDefiner::stack_size`]) must
/// be specified explicitly.
///
/// [`Bag`]: crate::bag::Bag
/// [`Hunk`]: crate::kernel::Hunk
pub struct StackHunk<System: cfg::KernelStatic>(super::Hunk<System>);
impl<System: cfg::KernelStatic> StackHunk<System> {
/// Construct `StackHunk`.
///
/// # Safety
///
/// When activating the assocaited task, the kernel will mutably borrow
/// the region starting at `hunk` without no borrow checking.
pub const unsafe fn new(hunk: super::Hunk<System>) -> Self {
Self(hunk)
}
/// Get the referenced [`Hunk`].
///
/// [`Hunk`]: crate::kernel::Hunk
#[inline]
pub const fn hunk(self) -> super::Hunk<System> {
self.0
}
}
impl<System: cfg::KernelStatic> Clone for StackHunk<System> {
#[inline]
fn clone(&self) -> Self {
Self(self.0)
}
}
impl<System: cfg::KernelStatic> Copy for StackHunk<System> {}