Skip to content

Commit

Permalink
Document task states and state transitions
Browse files Browse the repository at this point in the history
  • Loading branch information
bugadani committed Dec 17, 2024
1 parent c504ae8 commit b9feae2
Showing 1 changed file with 46 additions and 0 deletions.
46 changes: 46 additions & 0 deletions embassy-executor/src/raw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,46 @@ pub use self::waker::task_from_waker;
use super::SpawnToken;

/// Raw task header for use in task pointers.
///
/// A task can be in one of the following states:
///
/// - Not spawned: the task is ready to spawn.
/// - `SPAWNED`: the task is currently spawned and may be running.
/// - `RUN_ENQUEUED`: the task is enqueued to be polled. Note that the task may be `!SPAWNED`.
/// In this case, the `RUN_ENQUEUED` state will be cleared when the task is next polled, without
/// polling the task's future.
///
/// A task's complete life cycle is as follows:
///
/// ```text
/// ┌────────────┐ ┌────────────────────────┐
/// ┌─►│Not spawned │◄─6┤Not spawned|Run enqueued│
/// │ │ │ │ │
/// │ └─────┬──────┘ └──────▲─────────────────┘
/// │ 1 │
/// │ │ │
/// │ │ ┌────────────┘
/// │ │ │
/// │ │ 5
/// │ ┌─────▼────┴─────────┐
/// │ │Spawned|Run enqueued│
/// │ │ │
/// │ └─────┬▲─────────────┘
/// │ 2│
/// │ │3
/// │ ┌─────▼┴─────┐
/// └─4┤ Spawned │
/// │ │
/// └────────────┘
/// ```
///
/// Transitions:
/// - 1: Task is spawned - `AvailableTask::claim -> Executor::spawn`
/// - 2: During poll - `RunQueue::dequeue_all -> State::run_dequeue`
/// - 3: Task wakes itself, waker wakes task - `Waker::wake -> wake_task -> State::run_enqueue`
/// - 4: Task exits - `TaskStorage::poll -> Poll::Ready`
/// - 5: A run-queued task exits - `TaskStorage::poll -> Poll::Ready`
/// - 6: Task is dequeued and then ignored via `State::run_dequeue`
pub(crate) struct TaskHeader {
pub(crate) state: State,
pub(crate) run_queue_item: RunQueueItem,
Expand Down Expand Up @@ -507,6 +547,9 @@ pub fn wake_task(task: TaskRef) {
let header = task.header();
header.state.run_enqueue(|l| {
// We have just marked the task as scheduled, so enqueue it.
// FIXME: there is currently a data race between re-spawning a task and waking it using an
// old waker. If the task is being spawned on a different executor, then reading and writing
// the executor field may happen concurrently.
unsafe {
let executor = header.executor.load(Ordering::Relaxed).as_ref().unwrap_unchecked();
executor.enqueue(task, l);
Expand All @@ -521,6 +564,9 @@ pub fn wake_task_no_pend(task: TaskRef) {
let header = task.header();
header.state.run_enqueue(|l| {
// We have just marked the task as scheduled, so enqueue it.
// FIXME: there is currently a data race between re-spawning a task and waking it using an
// old waker. If the task is being spawned on a different executor, then reading and writing
// the executor field may happen concurrently.
unsafe {
let executor = header.executor.load(Ordering::Relaxed).as_ref().unwrap_unchecked();
executor.run_queue.enqueue(task, l);
Expand Down

0 comments on commit b9feae2

Please sign in to comment.