sched: replace the dead generation column with an enforced residency invariant#587
Merged
Conversation
…riant The module docstring advertised a generation-based lazy cancel via `cancel_all_for`, which does not exist. `PeripheralEntry.generation` was initialised to 0 everywhere and never bumped, so `is_stale` could only ever fire on its out-of-range branch and the generation comparison was `0 != 0` forever. The whole column -- field, scratch, snapshot plumbing through schedule/drain_due/next_event_deadline -- was dead weight that read as protection it never provided. Delete it, and document the contract that actually holds: scheduler-level identical-wake dedup (#570), per-peripheral in-flight singleton guards, and arm_seq arming tokens, with a table of which peripheral uses which. Document the residency caveat honestly: the dedup key includes the deadline, so re-arms at differing deadlines stay resident until they fire. Codify the bound that was previously only implicit. The scheduler now tracks live events per peripheral_idx and enforces a ceiling of 8 (MAX_LIVE_EVENTS_PER_PERIPHERAL, above dma's 7 channels): debug builds panic via debug_assert, release builds count the breach in SchedulerStats::live_event_ceiling_trips alongside a high-water mark. Regression tests cover the bounded re-arm patterns and prove the ceiling actually trips on unbounded distinct-deadline re-arming. Dedup semantics are unchanged: generation was a constant 0 in the key.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The scheduler's module docs advertise a generation-based cancel —
cancel_all_forbumps a per-peripheralgeneration,drain_duedrops stale events. That mechanism does not exist:cancel_all_foris absent from the crate, andPeripheralEntry.generationis initialised to 0 and never written, so the staleness comparison is0 != 0forever. The column is dead plumbing threaded throughschedule,drain_due,next_event_deadline, and the generation scratch buffers.What actually bounds the event heap is two undocumented per-peripheral patterns, plus the scheduler-level identical-wake dedup added for the SYSTIMER O(n²) fix. This replaces the fiction with the truth, and adds a structural guard so the leak class cannot silently return.
Removes
The
generationfield,generation_scratch,refresh_generation_scratch,peripheral_generations(),is_stale, the parameter onschedule/drain_due/drain_due_into/next_event_deadline, and the three tests exercising the dead path. Dedup semantics are unchanged — generation was a constant 0 in the key.Documents the real three-layer contract
queuedindex — the structural bound.scheduled/chain_live/drain_chain_armed) — i2c, uart, spi, scb, dma.arm_seq) — timer, systick, ledc.Includes the per-peripheral mechanism table so the next author knows which pattern to follow, and states the residency caveat honestly: same-deadline re-arms collapse; different-deadline re-arms stay resident until they fire — bounded by distinct in-flight reconfigurations, not unbounded.
Adds the invariant
MAX_LIVE_EVENTS_PER_PERIPHERAL = 8(above dma's 7 channels) with a per-index live count kept in lockstep with the heap, adebug_assertin debug builds, andSchedulerStats::{max_live_events_per_peripheral, live_event_ceiling_trips}in release.Verification
riscv_jit_c3_oled_differential: byte-identical over 30,000,000 instructions--ignored): 10 passed · clamped full-state: 3 passedevent-schedulerandjit,event-schedulerunbounded_distinct_rearm_trips_the_ceiling — should panic ... ok, plus bounded-pattern and subsystem-exemption tests