Expand description
Per-task execution-time accounting (plan.md Phase 10).
Built on the Group A cycle counter (crate::port::arch::cycle_count):
at every actual context switch, the cycles since the outgoing task’s
last dispatch are added to its running total. No accounting happens on
no-switch ticks (the common case) — this only costs a cycle_count()
read plus one add, and only when a switch was already going to happen
anyway.
RV32 and ARMv7-M both lack native 64-bit atomics (no AtomicU64 in
core for either target — the same gap rivet-arch-riscv::clint
documents for MTIME_HZ/TICK_PERIOD), so the cycle totals below are
plain static mut u64s rather than atomics. on_switch is only ever
called from crate::preempt::on_tick, itself only reached from trap/
exception context — structurally single-writer, since a hart can’t take
a second tick trap while already inside one. The one remaining hazard
is a task-context reader (report()) observing a torn write if a tick
lands mid-read; every read and write below is wrapped in
crate::critical::enter to rule that out (a harmless no-op nesting
when called from within on_tick, which already runs with interrupts
effectively masked).
Single global “last dispatch” stamp, matching the kernel’s current
single-CURRENT-task model (plan.md Phase 19 upgrades both together
for SMP).
Functions§
- busy_
cycles - Total cycles task
idhas spent running, since boot, as of its last completed dispatch — a task that is still running right now (never yet switched away from) is not included until its next switch. Budget enforcement needs the up-to-the-moment figure for whichever task is currently running; seebusy_cycles_live. - busy_
cycles_ live busy_cyclesplus the in-progress dispatch’s elapsed cycles, ifidis the currently-running task (checked by the caller — this function just adds “cycles since the single globalLAST_DISPATCHstamp”, which is only meaningful for whoever is actually running right now). Needed by [crate::deadlines::check_budget]: a task that never yields would otherwise never accumulate any completed-dispatch cycles at all, and its budget would never be checked.- busy_
percent - Integer percentage (0-100) of
cycles_since_boot()that taskidhas spent running.0before the preemptive tier starts or if the task hasn’t been dispatched yet. - cycles_
since_ boot - Cycles elapsed since the scheduler’s first dispatch (the denominator
for a
%busyfigure). Zero if the preemptive tier hasn’t started. - estimate_
us_ from_ cycles - Convert a cycle count into an estimated microsecond duration, using the
aggregate cycles-per-microsecond rate measured since boot
(
cycles_since_boot() / wallclock_us_since_boot()) — no board-declared clock-rate constant needed, and self-calibrating against whatever the cycle source actually is (realmcycle/DWT counting, or the microsecond-resolution SysTick fallback, in which case this is trivially exact). Used bycrate::deadlines’s budget enforcement to turn abudget_usinto a comparison againstbusy_cycleswithout needing a__rivet_arch_cycle_countcall-rate contract beyond “monotonic”. Zero before the preemptive tier starts, or if no time has passed yet (avoids a divide-by-zero). - on_
first_ dispatch - Record the very first dispatch (called once, from
crate::preempt::start). - on_
switch - Record an actual context switch away from
outgoing(its id inpreempt::tcb::TASKS), crediting it with the cycles since the last dispatch and resetting the stamp for whichever task runs next. No-op if accounting hasn’t started yet (on_first_dispatchnot called). - wallclock_
us_ since_ boot - Wall-clock microseconds elapsed since the scheduler’s first dispatch. Zero if the preemptive tier hasn’t started.