Skip to content

Commit b29f168

Browse files
committed
rmg-core/scheduler: fail-fast drain with unreachable!; restore u32 histogram; keep PendingTx private; docs follow-up
1 parent 9223f27 commit b29f168

File tree

4 files changed

+32
-27
lines changed

4 files changed

+32
-27
lines changed

crates/rmg-core/src/scheduler.rs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::tx::TxId;
2121
#[derive(Debug, Default)]
2222
pub(crate) struct DeterministicScheduler {
2323
/// Pending rewrites per transaction, stored for O(1) enqueue and O(n) drain.
24-
pub(crate) pending: HashMap<TxId, PendingTx<PendingRewrite>>,
24+
pending: HashMap<TxId, PendingTx<PendingRewrite>>,
2525
/// Generation-stamped conflict sets for O(1) independence checks.
2626
pub(crate) active: HashMap<TxId, GenSet<NodeId>>,
2727
#[cfg(feature = "telemetry")]
@@ -138,7 +138,7 @@ struct RewriteThin {
138138

139139
/// Pending transaction queue with O(1) enqueue and O(n) deterministic drain.
140140
#[derive(Debug)]
141-
pub(crate) struct PendingTx<P> {
141+
struct PendingTx<P> {
142142
next_nonce: u32,
143143
/// Last-wins dedupe on (`scope_hash`, `compact_rule`).
144144
index: FxHashMap<([u8; 32], u32), usize>,
@@ -148,9 +148,9 @@ pub(crate) struct PendingTx<P> {
148148
fat: Vec<Option<P>>,
149149
/// Scratch buffer for radix passes (reused).
150150
scratch: Vec<RewriteThin>,
151-
/// Counting array for 16-bit radix (65536 buckets, reused). Uses `usize`
152-
/// to avoid truncation and casts during prefix-sum scatter.
153-
counts16: Vec<usize>,
151+
/// Counting array for 16-bit radix (65536 buckets, reused). `u32` keeps
152+
/// bandwidth/cache lower while remaining ample for batch sizes we handle.
153+
counts16: Vec<u32>,
154154
}
155155

156156
impl<P> Default for PendingTx<P> {
@@ -205,7 +205,7 @@ impl<P> PendingTx<P> {
205205

206206
// Lazy allocation of 16-bit histogram (65536 buckets).
207207
if self.counts16.is_empty() {
208-
self.counts16 = vec![0usize; 1 << 16];
208+
self.counts16 = vec![0u32; 1 << 16];
209209
}
210210

211211
let mut flip = false;
@@ -226,7 +226,7 @@ impl<P> PendingTx<P> {
226226
}
227227

228228
// Prefix sums
229-
let mut sum: usize = 0;
229+
let mut sum: u32 = 0;
230230
for c in counts.iter_mut() {
231231
let t = *c;
232232
*c = sum;
@@ -236,9 +236,10 @@ impl<P> PendingTx<P> {
236236
// Stable scatter
237237
for r in src {
238238
let b = bucket16(r, pass) as usize;
239-
let idx = counts[b];
239+
let idx_u32 = counts[b];
240+
counts[b] = idx_u32.wrapping_add(1);
241+
let idx = idx_u32 as usize; // widening u32→usize (safe on 32/64-bit)
240242
dst[idx] = *r;
241-
counts[b] = idx + 1;
242243
}
243244

244245
flip = !flip;
@@ -264,14 +265,18 @@ impl<P> PendingTx<P> {
264265
let n = self.thin.len();
265266
let mut out = Vec::with_capacity(n);
266267
for r in self.thin.drain(..) {
267-
let payload_opt = self.fat[r.handle].take();
268-
// Invariant: every thin handle points to a live payload. Avoid
269-
// panicking on release builds; assert in debug to surface issues.
270-
if let Some(p) = payload_opt {
271-
out.push(p);
272-
} else {
273-
debug_assert!(false, "payload must exist");
274-
}
268+
// Invariant: each thin handle must point to a live payload.
269+
// If not, fail loudly to preserve determinism.
270+
let p = self.fat.get_mut(r.handle).map_or_else(
271+
|| unreachable!("BUG: handle out of range {}", r.handle),
272+
|slot| {
273+
slot.take().map_or_else(
274+
|| unreachable!("BUG: missing payload at handle {}", r.handle),
275+
|p| p,
276+
)
277+
},
278+
);
279+
out.push(p);
275280
}
276281
self.index.clear();
277282
self.fat.clear();

docs/decision-log.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
## Recent Decisions (2025-10-28 onward)
2020

2121
The following entries use a heading + bullets format for richer context.
22-
| 2025-11-06 | rmg-core scheduler Clippy cleanup | Make pre-commit pass without `--no-verify`: fix `doc_markdown`, `similar_names`, `if_not_else`, `option_if_let_else`, `explicit_iter_loop`; change `RewriteThin.handle` to `usize`; change radix `counts16` to `Vec<usize>`; remove `expect()` panic in drain (use `debug_assert!` + skip); mark `PendingTx<P>` as `pub(crate)`. | Preserve determinism and ordering while satisfying strict `clippy::pedantic` and `-D warnings`. Avoid truncation casts and private interface exposure. | Slight memory increase for radix counts on 64‑bit; no functional change intended; pre-commit unblocked.
22+
| 2025-11-06 | rmg-core scheduler Clippy cleanup | Make pre-commit pass without `--no-verify`: fix `doc_markdown`, `similar_names`, `if_not_else`, `option_if_let_else`, `explicit_iter_loop`; change `RewriteThin.handle` to `usize`; keep radix `counts16` as `Vec<u32>` (low bandwidth) with safe prefix-sum/scatter; fail fast in drain with `unreachable!` instead of `expect()` or silent drop; make `pending` field private (keep `PendingTx` private). | Preserve determinism and ordering while satisfying strict `clippy::pedantic` and `-D warnings`. Avoid truncation casts and private interface exposure. | Determinism preserved; panic on invariant violation; histogram remains 256 KiB on 64‑bit; pre-commit unblocked.
2323
| 2025-10-30 | rmg-core determinism hardening | Added reachability-only snapshot hashing; closed tx lifecycle; duplicate rule detection; deterministic scheduler drain order; expanded motion payload docs; tests for duplicate rule name/id and no‑op commit. | Locks determinism contract and surfaces API invariants; prepares PR #7 for a safe merge train. | Clippy clean for rmg-core; workspace push withheld pending further feedback. |
2424
| 2025-10-30 | Tests | Add golden motion fixtures (JSON) + minimal harness validating motion rule bytes/values | Establishes deterministic test baseline for motion; supports future benches and tooling | No runtime impact; PR-01 linked to umbrella and milestone |
2525
| 2025-10-30 | Templates PR scope | Clean `echo/pr-templates-and-project` to contain only templates + docs notes; remove unrelated files pulled in by merge; fix YAML lint (trailing blanks; quote placeholder) | Keep PRs reviewable and single-purpose; satisfy CI Docs Guard | Easier review; no runtime impact |

docs/echo-total.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,16 +260,16 @@ This is Codex’s working map for building Echo. Update it relentlessly—each s
260260

261261
## Today’s Intent
262262

263-
> 2025-11-06 — Unblock commit: rmg-core scheduler Clippy fixes
263+
> 2025-11-06 — Unblock commit: rmg-core scheduler Clippy fixes (follow-up)
264264
265265
- Goal: make pre-commit Clippy pass without `--no-verify`, preserving determinism.
266266
- Scope: `crates/rmg-core/src/scheduler.rs` only; no API surface changes intended.
267267
- Changes:
268268
- Fix doc lint: backticks for `scope_hash`, `rule_id`, `nonce`, `scope_be32`, `compact_rule`, `pair_idx_be`.
269-
- Privacy: mark `PendingTx<P>` as `pub(crate)` to match `DeterministicScheduler::pending` visibility.
269+
- Privacy: make `DeterministicScheduler::pending` private and keep `PendingTx<P>` private (no wider surface).
270270
- Pedantic lints: replace `if let/else` with `.map_or_else`, invert `if !flip` branch, iterate directly over slices, and avoid casts.
271-
- Safety: remove `expect()` on payload drain; use `debug_assert!` + skip in release to avoid panicking; document invariant.
272-
- Numerical: store radix `counts16` as `Vec<usize>` and `RewriteThin.handle` as `usize` to eliminate truncation casts.
271+
- Safety: fail fast in drain: replace `expect()` with `unreachable!(...)` via safe `get_mut(...).and_then(take)` to crash loudly on invariant break; no silent drops.
272+
- Numerical: keep `RewriteThin.handle` as `usize`; restore radix `counts16` to `Vec<u32>` to retain lower bandwidth/footprint while staying lint‑clean.
273273
- Expected behavior: identical drain order and semantics; minor memory increase for counts on 64‑bit.
274274
- Next: run full workspace Clippy + tests, then commit.
275275

@@ -643,7 +643,7 @@ Remember: every entry here shrinks temporal drift between Codices. Leave breadcr
643643
## Recent Decisions (2025-10-28 onward)
644644

645645
The following entries use a heading + bullets format for richer context.
646-
| 2025-11-06 | rmg-core scheduler Clippy cleanup | Make pre-commit pass without `--no-verify`: fix `doc_markdown`, `similar_names`, `if_not_else`, `option_if_let_else`, `explicit_iter_loop`; change `RewriteThin.handle` to `usize`; change radix `counts16` to `Vec<usize>`; remove `expect()` panic in drain (use `debug_assert!` + skip); mark `PendingTx<P>` as `pub(crate)`. | Preserve determinism and ordering while satisfying strict `clippy::pedantic` and `-D warnings`. Avoid truncation casts and private interface exposure. | Slight memory increase for radix counts on 64‑bit; no functional change intended; pre-commit unblocked.
646+
| 2025-11-06 | rmg-core scheduler Clippy cleanup | Make pre-commit pass without `--no-verify`: fix `doc_markdown`, `similar_names`, `if_not_else`, `option_if_let_else`, `explicit_iter_loop`; change `RewriteThin.handle` to `usize`; keep radix `counts16` as `Vec<u32>` (low bandwidth) with safe prefix-sum/scatter; fail fast in drain with `unreachable!` instead of `expect()` or silent drop; make `pending` field private (keep `PendingTx` private). | Preserve determinism and ordering while satisfying strict `clippy::pedantic` and `-D warnings`. Avoid truncation casts and private interface exposure. | Determinism preserved; panic on invariant violation; histogram remains 256 KiB on 64‑bit; pre-commit unblocked.
647647
| 2025-10-30 | rmg-core determinism hardening | Added reachability-only snapshot hashing; closed tx lifecycle; duplicate rule detection; deterministic scheduler drain order; expanded motion payload docs; tests for duplicate rule name/id and no‑op commit. | Locks determinism contract and surfaces API invariants; prepares PR #7 for a safe merge train. | Clippy clean for rmg-core; workspace push withheld pending further feedback. |
648648
| 2025-10-30 | Tests | Add golden motion fixtures (JSON) + minimal harness validating motion rule bytes/values | Establishes deterministic test baseline for motion; supports future benches and tooling | No runtime impact; PR-01 linked to umbrella and milestone |
649649
| 2025-10-30 | Templates PR scope | Clean `echo/pr-templates-and-project` to contain only templates + docs notes; remove unrelated files pulled in by merge; fix YAML lint (trailing blanks; quote placeholder) | Keep PRs reviewable and single-purpose; satisfy CI Docs Guard | Easier review; no runtime impact |

docs/execution-plan.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@ This is Codex’s working map for building Echo. Update it relentlessly—each s
3333

3434
## Today’s Intent
3535

36-
> 2025-11-06 — Unblock commit: rmg-core scheduler Clippy fixes
36+
> 2025-11-06 — Unblock commit: rmg-core scheduler Clippy fixes (follow-up)
3737
3838
- Goal: make pre-commit Clippy pass without `--no-verify`, preserving determinism.
3939
- Scope: `crates/rmg-core/src/scheduler.rs` only; no API surface changes intended.
4040
- Changes:
4141
- Fix doc lint: backticks for `scope_hash`, `rule_id`, `nonce`, `scope_be32`, `compact_rule`, `pair_idx_be`.
42-
- Privacy: mark `PendingTx<P>` as `pub(crate)` to match `DeterministicScheduler::pending` visibility.
42+
- Privacy: make `DeterministicScheduler::pending` private and keep `PendingTx<P>` private (no wider surface).
4343
- Pedantic lints: replace `if let/else` with `.map_or_else`, invert `if !flip` branch, iterate directly over slices, and avoid casts.
44-
- Safety: remove `expect()` on payload drain; use `debug_assert!` + skip in release to avoid panicking; document invariant.
45-
- Numerical: store radix `counts16` as `Vec<usize>` and `RewriteThin.handle` as `usize` to eliminate truncation casts.
44+
- Safety: fail fast in drain: replace `expect()` with `unreachable!(...)` via safe `get_mut(...).and_then(take)` to crash loudly on invariant break; no silent drops.
45+
- Numerical: keep `RewriteThin.handle` as `usize`; restore radix `counts16` to `Vec<u32>` to retain lower bandwidth/footprint while staying lint‑clean.
4646
- Expected behavior: identical drain order and semantics; minor memory increase for counts on 64‑bit.
4747
- Next: run full workspace Clippy + tests, then commit.
4848

0 commit comments

Comments
 (0)