Skip to main content

Module preempt

Module preempt 

Source
Expand description

Preemptive tier: tasks with dedicated stacks, real priority preemption.

Unlike #[rivet::task] (cooperative — only yields at .await), a preemptive task can be suspended by the timer tick at any point and resumed later from exactly there, because its full execution context (registers + program counter + stack pointer) is saved/restored on every switch. This is what lets a genuinely higher-priority task interrupt a lower-priority one that never calls anything cooperative.

All switching — tick-driven preemption and voluntary yields (blocking on a mutex, explicit port::arch::request_reschedule()) — goes through the same interrupt/trap path (software interrupt on RISC-V, PendSV on Cortex-M). There’s no separate “synchronous context switch function call” — the arch trap handler saves the interrupted task’s full context, asks on_tick which task to resume, and returns to that task’s saved context. This matches how real embedded RTOS ports (FreeRTOS, etc.) implement it, and keeps there being exactly one code path that has to be correct instead of two.

The cooperative async executor still exists — it runs as an ordinary preemptive task at the lowest priority (see crate::init), so any real preemptive task immediately preempts it, and it fills otherwise-idle CPU time with async work.

Re-exports§

pub use mutex::PriorityMutex;
pub use mutex::PriorityMutexGuard;
pub use tcb::TaskState;
pub use lifecycle::JoinError;

Modules§

lifecycle
Task lifecycle: exit, join, stop (plan.md §5).
mutex
Priority-inheritance mutex for the preemptive tier.
sched
Priority + round-robin scheduler for the preemptive tier.
stack_pool
Preemptive task stack pool (plan.md §3).
tcb
Task Control Block and the preemptive task registry.

Structs§

Stack
Statically-sized, correctly-aligned stack storage for a preemptive task.
TaskHandle
Handle to a spawned preemptive task: the registry slot id plus the slot’s generation counter, so stale handles can be detected after the slot was recycled (plan.md §4.3 / §5.1).

Enums§

SpawnError
Errors from spawn / [macro@spawn_ptask] — fixed-size resources degrade with a typed error, never a silent drop or panic (plan.md §4.3).

Functions§

on_tick
Called from the arch trap/exception handler (timer tick, or a software interrupt triggered by crate::port::arch::request_reschedule) with the interrupted task’s just-saved stack pointer. Consults the scheduler and returns the stack pointer the arch layer should actually resume — either the same one (no reschedule needed) or a different task’s (real preemption / voluntary switch).
park_forever
sleep_ms
Permanently remove the current preemptive task from scheduling. Useful for a task that does bounded work and then has nothing left to do — parking (rather than spinning forever at its original priority) lets lower-priority tasks actually run.
sleep_until
Block the calling preemptive task until the absolute time deadline_us (plan.md §5.6 / Phase 11). sleep_ms is sleep_until(now + ms*1000); crate::deadlines::wait_period uses this directly with a drift-corrected deadline so periodic jitter doesn’t accumulate. No-op outside a preemptive task context. If deadline_us has already passed, still yields once (bounded, not a busy spin) rather than returning immediately.
spawn
Spawn a preemptive task with its own stack.
stack_usage
Measure a task stack’s high-water mark (plan.md §2.7/Phase 3): stacks are filled with 0xAA at spawn; the deepest byte a task wrote (anything else) marks how far it ran down. Returns the number of bytes used from the top.
start
Start the preemptive scheduler. Never returns — control transfers permanently to whichever task the scheduler selects first (and from there, forever between tasks via interrupt-driven context switches).
start_secondary_hart
Start the preemptive scheduler on a secondary hart (plan.md Phase 19). Identical to start except a hart that finds nothing ready yet idles and retries instead of panicking: unlike hart 0 (which only starts after at least one task has been spawned), a secondary hart legitimately has nothing to do until some hart makes a task ready and IPIs it via port::arch::request_reschedule_on.