feat(safety): phase 5 — loom model checking + invariant audit/assertion sweep
State machine extracted to src/slot_state.rs as a standalone unit (StateWord: publish_queued / try_claim / yield_return / park_return / unpark / set_done / reclaim, plus the Status view for cold paths). runtime.rs keeps the protocol rationale and consumes the mechanism; every transition self-asserts its precondition per the assert-the-invariants rule. src/sync_shim.rs: std vs loom indirection (atomics + UnsafeCell with the with/with_mut access API) for the two loom-modeled modules. Loom models run the PRODUCTION code, not a replica: - slot_state: no-lost-wakeup (park vs unpark), two-unparkers-one-enqueue, stale-unpark-never-hits-reused-slot (the ABA theorem), unpark-vs-claim. - run_queue: mpmc exactly-once through a lap wraparound, push/pop race, striped two-producer drain. RUSTFLAGS="--cfg loom" cargo test --lib --release — 7 models, all pass. RawMutex deliberately not loom-modeled (futexes can't be; textbook mutex3 with stress + unwind coverage). Assertion sweep (invariants now checked at the point of reliance): - enqueue debug-asserts the word reads EXACTLY (gen, Queued) — the at-most-once-enqueued invariant the ring capacity proof leans on. - RawMutex enforces the leaf rule mechanically: debug-build thread-local held-count, panics at the acquisition that violates it. - live_actors underflow (double finalize) asserted. - StateWord transition preconditions asserted (yield/park/done/reclaim/ publish/claim). Audit: with_runtime, RawMutex guards, run-queue ops, and trace::record all gate preemption (and thereby the stop sentinel) for their span; trace was already self-gating. Sole remaining exception is the channel std MutexGuard — the documented first post-v0.5 fast-follow. Review fixes to the branched-in work: - slot_state::status_for mapped (matching gen, Vacant) to Stale instead of unreachable!: Pid::new is public, so a forged/never-issued pid (e.g. monitor(Pid::new(5,0)) on a fresh slab) could reach it — "no such actor" is the correct total answer; issued pids still can't get there. - Tightened enqueue's assert from Status::Live to the exact (gen, Queued) word its own comment argues for. Validated: 22 suites in debug (all asserts + leaf counter live) for rq-mutex/rq-mpmc/rq-striped, release for rq-mutex, smarm-trace build + stress, loom 7/7.
This commit is contained in:
+20
-5
@@ -98,11 +98,26 @@ Make slot lookup lock-free and per-slot state independently mutable.
|
||||
contention curves and the actual variant decision come from the
|
||||
20-core box.
|
||||
|
||||
## Phase 5 — Safety hardening & model checking
|
||||
- [ ] `loom` (dev-dependency only, x86 Linux) model tests for the slot state
|
||||
machine and each ring variant.
|
||||
- [ ] `NoPreempt` / no-unwind-under-lock audit across all internal critical
|
||||
sections; assert the invariant in debug builds where feasible.
|
||||
## Phase 5 — Safety hardening & model checking ✅ DONE
|
||||
- [x] State machine extracted to `src/slot_state.rs` (mechanism only; the
|
||||
protocol rationale stays in `runtime.rs`) so loom checks the PRODUCTION
|
||||
transitions. Models: lost-wakeup (park vs unpark), at-most-once
|
||||
(two unparkers), ABA (stale unpark vs reclaim+reuse), claim coalescing.
|
||||
- [x] Loom on the rings via `src/sync_shim.rs` (std normally, `loom::sync`
|
||||
under `--cfg loom`): exactly-once through lap wraparound, push/pop
|
||||
race, striped two-producer drain. `[target.'cfg(loom)'.dependencies]`;
|
||||
run with `RUSTFLAGS="--cfg loom" cargo test --lib --release`.
|
||||
RawMutex is deliberately NOT loom-modeled: futexes can't be, and it is
|
||||
Drepper's textbook mutex3 with stress + unwind tests.
|
||||
- [x] NoPreempt / no-unwind / TLS-guard audit: with_runtime + RawMutex guards
|
||||
+ run-queue ops + trace::record all gate preemption (and thereby the
|
||||
stop sentinel) for their span. Sole remaining exception: channel's std
|
||||
MutexGuard — the documented first fast-follow.
|
||||
- [x] Invariant-assertion sweep (the fast-follow, pulled forward): every
|
||||
StateWord transition self-asserts its precondition; enqueue asserts
|
||||
exactly (gen, Queued); live_actors underflow asserted; RawMutex
|
||||
enforces the leaf rule with a debug-build held-count; slab overflow and
|
||||
ring overflow stay loud panics. House style from here on.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user