kernel::scheduler
Preemptive priority scheduler.
48 application priority levels plus a reserved idle level (lower number = higher priority), round-robin within a level via a 10 ms quantum. Supports priority inheritance, sleep, queue/mutex blocking, and a pending-switch flag consumed by the trap handler.
Switching model (plan W1.2): all context switches happen in the trap
handler (crate::switch::flint_trap) and resume via rfe. Cooperative
operations (sleep/yield/block) mutate scheduler state then raise the software
interrupt so the switch occurs through the same path. Nothing switches by
returning up a call stack anymore.
Concurrency (plan W2.2): the global scheduler is mutated either from task
context inside a cs_with critical section, or from the trap handler with
interrupts already masked. Both exclude each other.
Structs
Section titled “Structs”Scheduler
Section titled “Scheduler”struct Scheduler { pub tasks: [Option<TaskControlBlock>; 32], pub current_per_core: [u32; 2], pub ready_mask: u64, // some private fields omitted}Implements: Default
References: TaskControlBlock
TaskControlBlock
Section titled “TaskControlBlock”struct TaskControlBlock { pub id: u32, pub name: &'static str, pub entry: Option<fn(..)>, pub affinity: Affinity, pub base_prio: u8, pub priority: u8, pub state: TaskState, pub stack_base: u32, pub stack_size: u32, pub stack_hwm: u32, pub context: crate::arch::Context, pub quantum: u32, pub sleep_until: u64, pub boosted_from: Option<u8>, pub blocked_on_mutex: Option<usize>, pub heap_stack: bool,}Per-task control block.
References: Affinity, Context, TaskState
Affinity
Section titled “Affinity”enum Affinity { Any, Core(hal::smp::CoreId),}Which core(s) a task may run on.
The default is [Affinity::Any], because most tasks genuinely do not care
and pinning what does not need pinning throws away the second core.
Pinning exists for the ones that do: a driver whose peripheral interrupt is routed to one core’s matrix cannot service it from the other, and anything with a hard timing budget does not want to be moved.
Implements: Clone, Copy, Debug, Default, Eq, PartialEq, StructuralPartialEq
References: CoreId
TaskState
Section titled “TaskState”enum TaskState { Init, Ready, Running, BlockedSend, BlockedRecv, BlockedMutex, BlockedSleep, Suspended, Deleting, Faulted,}Implements: Clone, Copy, Debug, Eq, PartialEq, StructuralPartialEq
Functions
Section titled “Functions”join_current_core
Section titled “join_current_core”fn join_current_core(idle: u32)Attach the calling secondary core to an already-created pinned idle task.
The joined bit is deliberately the last write. Until the task is both
Running and installed as this core’s current, affinity requests must
continue to reject the core.
request_switch
Section titled “request_switch”fn request_switch()Request a context switch: set the flag and raise the software interrupt so the switch happens in the trap handler.
request_switch_on
Section titled “request_switch_on”fn request_switch_on(core: hal::smp::CoreId)Request a switch on another joined core.
The pending flag is published before the inter-core notification. If the architecture has no remote interrupt hook, the next periodic tick still observes the request.
References: CoreId
set_pending_switch
Section titled “set_pending_switch”fn set_pending_switch()Record that a switch is owed.
set_pending_switch_on
Section titled “set_pending_switch_on”fn set_pending_switch_on(core: hal::smp::CoreId)Record scheduling work for a particular joined core.
References: CoreId
switch_pending
Section titled “switch_pending”fn switch_pending() -> boolWhether this core has scheduling work pending, without consuming it.
take_pending_switch
Section titled “take_pending_switch”fn take_pending_switch() -> boolConsume the pending-switch flag, returning whether one was owed.
try_with
Section titled “try_with”fn try_with<R, impl FnOnce(&mut Scheduler) -> R>(f: impl FnOnce) -> Option<R>Run f only if the scheduler is free, rather than waiting.
For diagnostics that must not hang — a fault handler reporting task state cannot afford to block on a lock whose holder may be the thing that faulted.
fn with<R, impl FnOnce(&mut Scheduler) -> R>(f: impl FnOnce) -> RRun f with the scheduler locked. Safe from task or interrupt context, on
either core.
Keep f short: interrupts are masked on this core throughout, and the
other core spins if it wants the scheduler meanwhile.
Constants
Section titled “Constants”DEFAULT_QUANTUM_MS
Section titled “DEFAULT_QUANTUM_MS”const DEFAULT_QUANTUM_MS: u32;IDLE_PRIORITY
Section titled “IDLE_PRIORITY”const IDLE_PRIORITY: u8;The lowest possible priority value (idle). Higher number = lower priority.
Deliberately one below anything application code can request. When this was
47 it collided with Background(15), so a task spawned at the lowest public
priority became a round-robin peer of idle and shared time-slices with it,
rather than idle being a guaranteed last resort.
MAX_PUBLIC_PRIORITY
Section titled “MAX_PUBLIC_PRIORITY”const MAX_PUBLIC_PRIORITY: u8;Highest priority value any public Priority can encode to
(Background(15) == 0x2F).
MAX_TASKS
Section titled “MAX_TASKS”const MAX_TASKS: usize;NUM_PRIORITIES
Section titled “NUM_PRIORITIES”const NUM_PRIORITIES: usize;Effective priority levels.
The public Priority encoding spans 0x00..=0x2F (3 bands × 16 levels, see
Priority::numeric), so 48 values are reachable from application code. One
further level exists above them, reserved for idle – hence 49.