Skip to main content

Module exec_time

Module exec_time 

Source
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 id has 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; see busy_cycles_live.
busy_cycles_live
busy_cycles plus the in-progress dispatch’s elapsed cycles, if id is the currently-running task (checked by the caller — this function just adds “cycles since the single global LAST_DISPATCH stamp”, 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 task id has spent running. 0 before 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 %busy figure). 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 (real mcycle/DWT counting, or the microsecond-resolution SysTick fallback, in which case this is trivially exact). Used by crate::deadlines’s budget enforcement to turn a budget_us into a comparison against busy_cycles without needing a __rivet_arch_cycle_count call-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 in preempt::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_dispatch not called).
wallclock_us_since_boot
Wall-clock microseconds elapsed since the scheduler’s first dispatch. Zero if the preemptive tier hasn’t started.