CI: warm ahash process statics before the capi lifecycle allocator oracle arms - #392
Conversation
…acle arms json-syntax 0.12.5 indexes objects through hashbrown 0.12's DefaultHashBuilder, ahash 0.7's RandomState, whose first construction in a process boxes three once_cell OnceBox statics (88 bytes total) that live until exit. Whichever of resource_lifecycle.rs's three tests parses the first object pays them, which under the parallel harness races against sibling tests and fails the exact allocation/deallocation-count assertion in exported_c_candidates_replay_render_and_both_destroy_orders_balance_exactly. Parses a trivial object before begin() arms observation, so every window starts after that initialization on this thread regardless of scheduling. Commands run: RUST_TEST_THREADS=1 cargo test --locked -p capi --test resource_lifecycle x30 on origin/main (30/30 failed) and x30 on this branch (30/30 passed); the single failing test with --exact x30 on this branch (30/30 passed); the CI job's exact "workspace debug tests" command once on this branch (all green); cargo test --locked -p capi -p session; cargo fmt --all -- --check; cargo clippy --locked -p capi --all-targets -- -D warnings; git diff --check; and the full "fmt, clippy, doc, and hermetic policy gates" job's command list once (30/30 steps passed). Rebuilt scripts/build-web-audioworklet.sh and confirmed the AudioWorklet artifact digest is unchanged (no shipped file changed). Refs #385 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwL1uTcxsmopHtamG6bMko
tamashi095
left a comment
There was a problem hiding this comment.
Verifier verdict: APPROVE (Fable 5.1, x86_64; comment review because the PR and the review share an account).
Diff is 16 test lines in crates/capi/tests/resource_lifecycle.rs (a warm-up that parses {"warm":0} through session::parse_session_json before the allocator oracle arms) and one doc sentence; no shipped file, artifact digest unchanged.
Reproduced and verified independently in fresh worktrees: RUST_TEST_THREADS=1 cargo test -p capi --test resource_lifecycle fails 5 of 5 on origin/main 65d83af with the CI's exact numbers (left: 2613 right: 2610), and passes 10 of 10 on this branch; the failing test alone passes 10 of 10 on the branch. Root cause per the diagnosis: json-syntax 0.12.5 indexes objects through hashbrown 0.12 / ahash 0.7 RandomState, whose first construction in a process boxes three process-lifetime statics (8 + 16 + 64 bytes); whichever test thread parses the first object pays them inside its window. Shipped caps are explicit row sums (compile.rs:185) and unaffected. {"warm":0} rather than {} is correct because #390 will make the preflight refuse {} before the parser runs.
Summary
crates/capi/tests/resource_lifecycle.rs'sexported_c_candidates_replay_render_and_both_destroy_orders_balance_exactlyasserts an exactallocation-call count against an exact deallocation-call count inside an armed window. After #385
replaced
jstrictwith upstreamjson-syntax 0.12.5, every object parse indexes throughhashbrown0.12'sDefaultHashBuilder, which isahash0.7'sRandomState. Its firstconstruction in a process boxes three
once_cell::race::OnceBoxstatics (RAND_SOURCE, its innerBox<dyn RandomSource>, and theSEEDSarray: 8 + 16 + 64 = 88 bytes) that live until processexit and belong to no capi owner.
ahash'sbuild.rsforces theruntime-rngfeature on everyhosted target, so no Cargo feature removes it.
Those 88 bytes land on whichever thread parses the first
json-syntaxobject in the process.Under the parallel test harness that's a race between this file's three tests; on the CI runner
(4 vCPU)
exported_c_candidates_replay_render_and_both_destroy_orders_balance_exactlyloses therace and gets charged for an allocation with no matching deallocation inside its own window
(
left: 2613 right: 2610, deterministic withRUST_TEST_THREADS=1).Fix (test-only): parse a trivial JSON object once, before
begin()arms the allocatororacle, so the lazy
ahashstatics are always initialized before any window starts observing.Uses
{"warm":0}, not{}— PR #390 (landing soon) makes the preflight refuse an empty objectbefore the parser runs, which would defeat the warm-up;
{"warm":0}reachesjson_syntax::Value::parse_strand constructs the hasher regardless of what the preflight orschema does with it afterward.
Shipped caps are unaffected. They are explicit row sums computed at compile time
(
crates/capi/src/runtime/compile.rs:185), not derived from any allocator oracle, and this PRtouches no shipped file — confirmed by rebuilding the AudioWorklet artifact and diffing its digest
against the committed pin (unchanged, see Gate output).
Also adds one sentence to
docs/REALTIME_DEPENDENCY_POLICY.md's #385 session-parser entryrecording this lazy-allocation behavior, so a future reader of that policy (or a future allocator
oracle) isn't surprised by it again.
Before / after
RUST_TEST_THREADS=1 cargo test --locked -p capi --test resource_lifecycle, 30 runs each:origin/main(unfixed, this branch's parent before the fix commit)The failing test alone,
--exact, 30 runs on this branch (fixed):exported_c_candidates_replay_render_and_both_destroy_orders_balance_exactlyGate output
All run on this branch, from the worktree root,
RUST_TEST_THREADSunset except where noted:RUST_TEST_THREADS=1 cargo test --locked -p capi --test resource_lifecyclex30 — 30/30 passed (see Before/after)RUST_TEST_THREADS=1 cargo test --locked -p capi --test resource_lifecycle -- --exact exported_c_candidates_replay_render_and_both_destroy_orders_balance_exactlyx30 — 30/30 passedtest-debug-a's exact "Workspace debug tests" command (cargo test --locked --workspace --all-targets --exclude lane --exclude math --exclude effect-runtime --exclude delay --exclude compressor --exclude multiband-compressor --exclude gate-expander --exclude true-peak-limiter --exclude transient-shaper --exclude soft-clip --exclude parametric-eq --exclude builtins --exclude dsp-reference --exclude conformance --exclude audit --exclude bench --exclude console-workload --exclude wasm-gates --exclude wasm-console --exclude wasm-gate-guest --exclude wasm-console-guest --exclude wasm-gate-corpus --features builtins-compiler/test-support,source/test-support,graph/test-support,engine/realtime-audit) — PASS, 98/98 test groups green, 0 failedcargo run --locked -p host-native(same job's "Native host smoke" step) — PASScargo test --locked -p capi -p session— PASS, all groups greencargo fmt --all -- --check— PASScargo clippy --locked -p capi --all-targets -- -D warnings— PASS, no warningsgit diff --check— PASS, no whitespace errorslintjob ("fmt, clippy, doc, and hermetic policy gates") command list, run once, one line per command — 30/30 steps PASS:cargo fmt --all -- --check— PASScargo clippy --locked --workspace --all-targets --all-features -- -D warnings— PASSRUSTDOCFLAGS='-D warnings' cargo doc --locked --workspace --no-deps— PASSbash scripts/check-workspace-policy.sh— PASSbash scripts/test-workspace-policy.sh— PASSbash scripts/check-session-policy.sh— PASSbash scripts/check-env-vocabulary.sh && bash scripts/test-env-vocabulary.sh— PASSbash scripts/check-bench-policy.sh && bash scripts/test-bench-policy.sh— PASSbash scripts/check-console-benchmark-fixture.sh && bash scripts/check-rack-benchmark-fixture.sh— PASSbash scripts/test-builtins-fixtures.sh— PASSbash scripts/test-wasm-console-benchmark.sh— PASSbash scripts/check-bench-preconditions.sh && bash scripts/test-console-benchmark.sh && bash scripts/test-rack-benchmark.sh && bash scripts/test-wasm-kernel-timing.sh— PASSbash scripts/check-host-core-policy.sh && bash scripts/test-host-core-policy.sh— PASSbash scripts/check-protocol-control-policy.sh && bash scripts/test-protocol-control-policy.sh— PASSbash scripts/check-realtime-policy.sh && bash scripts/test-realtime-policy.sh && bash scripts/check-realtime-audit-leak.sh && bash scripts/test-realtime-audit-leak.sh && bash scripts/check-artifact-evidence-leak.sh && bash scripts/test-artifact-evidence-leak.sh— PASSbash scripts/test-realtime-trace-validator.sh— PASSbash scripts/check-lane-policy.sh && bash scripts/test-lane-policy.sh— PASSbash scripts/check-unfused-seal.sh && bash scripts/check-unfused-seal.sh --self-test— PASSbash scripts/check-rack-policy.sh && bash scripts/test-rack-policy.sh && bash scripts/check-builtins-policy.sh && bash scripts/test-builtins-policy.sh && bash scripts/check-graph-policy.sh— PASSbash scripts/check-effect-runtime-policy.sh && bash scripts/test-effect-runtime-policy.sh && bash scripts/check-effect-runtime-fixtures.sh && bash scripts/test-effect-runtime-fixtures.sh— PASSbash scripts/check-effect-interchange-qualification.sh && bash scripts/test-effect-interchange-policy.sh— PASSbash scripts/check-native-pcm-runner.sh && bash scripts/test-native-pcm-runner-v1-policy.sh && bash scripts/test-native-pcm-runner-portability-v1-policy.sh— PASSbash scripts/check-conformance-boundaries.sh— PASSpython3 -B scripts/check-step-vocabulary.py && python3 -B scripts/check-step-vocabulary.py --self-test— PASSbash scripts/check-parametric-eq-render-contract.sh— PASSpython3 -B scripts/check-release-shape.py --self-test— PASSnode scripts/check-stem-store-v1.mjs— PASSCARGO_TARGET_DIR=target/ci/x86-scalar RUSTFLAGS='-C target-cpu=x86-64 -C target-feature=-avx2,-fma' cargo check --locked -p engine -p target-smoke -p mathrefused withrequires x86-64-v3, cfg assertions held — PASS+avx2,-fma— PASS+avx2,+fma,-p engine -p target-smoke -p math -p builtins -p builtins-compiler -p effect-runtime -p gate-expander -p multiband-compressor -p parametric-eq -p soft-clip) and cfg assertions — PASSbash scripts/build-web-audioworklet.sh <dir>rebuilt clean against the committed pin (hosts/host-web/web/miso-engine-v1-audio-worklet-artifact.sha256,22e4c25c...) with noMISO_ENGINE_WEB_AUDIOWORKLET_REPINoverride — pin matched, no shipped file changed.bash scripts/check-web-audioworklet.sh <dir>— PASS.Seen, not done
json-syntaxhard-codeshashbrown's defaultahashhasher; there is no Cargo feature orcall-site override that removes the lazily-allocated statics. A static-free (or eagerly
initialized) hasher would need an upstream change to
json-syntaxor a fork, which is out ofscope for a test-only CI fix. #391 already tracks a separate upstream
json-syntaxdefect (theempty-object
CodeMapvolume bug from #387/#390) and shows the shape a follow-up like this wouldtake (upstream PR, optional
[patch]ruling againstdocs/REALTIME_DEPENDENCY_POLICY.md,#[ignore]d regression test until it ships); no equivalent tracking issue exists yet for theahashhasher statics this PR works around in-tree.Skipped
sentence.
🤖 Generated with Claude Code
https://claude.ai/code/session_01EwL1uTcxsmopHtamG6bMko
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.