Skip to content

CI: warm ahash process statics before the capi lifecycle allocator oracle arms - #392

Merged
tamashi095 merged 1 commit into
mainfrom
sonnet/ci-385-lifecycle-warmup
Sep 4, 2026
Merged

CI: warm ahash process statics before the capi lifecycle allocator oracle arms#392
tamashi095 merged 1 commit into
mainfrom
sonnet/ci-385-lifecycle-warmup

Conversation

@tamashi095

@tamashi095 tamashi095 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Summary

crates/capi/tests/resource_lifecycle.rs's
exported_c_candidates_replay_render_and_both_destroy_orders_balance_exactly asserts an exact
allocation-call count against an exact deallocation-call count inside an armed window. After #385
replaced jstrict with upstream json-syntax 0.12.5, every object parse indexes through
hashbrown 0.12's DefaultHashBuilder, which is ahash 0.7's RandomState. Its first
construction in a process boxes three once_cell::race::OnceBox statics (RAND_SOURCE, its inner
Box<dyn RandomSource>, and the SEEDS array: 8 + 16 + 64 = 88 bytes) that live until process
exit and belong to no capi owner. ahash's build.rs forces the runtime-rng feature on every
hosted target, so no Cargo feature removes it.

Those 88 bytes land on whichever thread parses the first json-syntax object 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_exactly loses the
race and gets charged for an allocation with no matching deallocation inside its own window
(left: 2613 right: 2610, deterministic with RUST_TEST_THREADS=1).

Fix (test-only): parse a trivial JSON object once, before begin() arms the allocator
oracle, so the lazy ahash statics are always initialized before any window starts observing.
Uses {"warm":0}, not {} — PR #390 (landing soon) makes the preflight refuse an empty object
before the parser runs, which would defeat the warm-up; {"warm":0} reaches
json_syntax::Value::parse_str and constructs the hasher regardless of what the preflight or
schema 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 PR
touches 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 entry
recording 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:

Tree Runs Pass Fail
origin/main (unfixed, this branch's parent before the fix commit) 30 0 30
this branch (fixed) 30 30 0

The failing test alone, --exact, 30 runs on this branch (fixed):

Test Runs Pass Fail
exported_c_candidates_replay_render_and_both_destroy_orders_balance_exactly 30 30 0

Gate output

All run on this branch, from the worktree root, RUST_TEST_THREADS unset except where noted:

  • RUST_TEST_THREADS=1 cargo test --locked -p capi --test resource_lifecycle x30 — 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_exactly x30 — 30/30 passed
  • CI job test-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 failed
  • cargo run --locked -p host-native (same job's "Native host smoke" step) — PASS
  • cargo test --locked -p capi -p session — PASS, all groups green
  • cargo fmt --all -- --check — PASS
  • cargo clippy --locked -p capi --all-targets -- -D warnings — PASS, no warnings
  • git diff --check — PASS, no whitespace errors
  • Full lint job ("fmt, clippy, doc, and hermetic policy gates") command list, run once, one line per command — 30/30 steps PASS:
    • cargo fmt --all -- --check — PASS
    • cargo clippy --locked --workspace --all-targets --all-features -- -D warnings — PASS
    • RUSTDOCFLAGS='-D warnings' cargo doc --locked --workspace --no-deps — PASS
    • bash scripts/check-workspace-policy.sh — PASS
    • bash scripts/test-workspace-policy.sh — PASS
    • bash scripts/check-session-policy.sh — PASS
    • bash scripts/check-env-vocabulary.sh && bash scripts/test-env-vocabulary.sh — PASS
    • bash scripts/check-bench-policy.sh && bash scripts/test-bench-policy.sh — PASS
    • bash scripts/check-console-benchmark-fixture.sh && bash scripts/check-rack-benchmark-fixture.sh — PASS
    • bash scripts/test-builtins-fixtures.sh — PASS
    • bash scripts/test-wasm-console-benchmark.sh — PASS
    • bash scripts/check-bench-preconditions.sh && bash scripts/test-console-benchmark.sh && bash scripts/test-rack-benchmark.sh && bash scripts/test-wasm-kernel-timing.sh — PASS
    • bash scripts/check-host-core-policy.sh && bash scripts/test-host-core-policy.sh — PASS
    • bash scripts/check-protocol-control-policy.sh && bash scripts/test-protocol-control-policy.sh — PASS
    • bash 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 — PASS
    • bash scripts/test-realtime-trace-validator.sh — PASS
    • bash scripts/check-lane-policy.sh && bash scripts/test-lane-policy.sh — PASS
    • bash scripts/check-unfused-seal.sh && bash scripts/check-unfused-seal.sh --self-test — PASS
    • bash 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 — PASS
    • bash 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 — PASS
    • bash scripts/check-effect-interchange-qualification.sh && bash scripts/test-effect-interchange-policy.sh — PASS
    • bash 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 — PASS
    • bash scripts/check-conformance-boundaries.sh — PASS
    • python3 -B scripts/check-step-vocabulary.py && python3 -B scripts/check-step-vocabulary.py --self-test — PASS
    • bash scripts/check-parametric-eq-render-contract.sh — PASS
    • python3 -B scripts/check-release-shape.py --self-test — PASS
    • node scripts/check-stem-store-v1.mjs — PASS
    • Sub-v3 refusal (scalar): CARGO_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 math refused with requires x86-64-v3, cfg assertions held — PASS
    • Sub-v3 refusal (AVX2 without FMA): same shape with +avx2,-fma — PASS
    • AVX2+FMA compile probe (+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 — PASS
  • No lineage re-pin needed: bash 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 no MISO_ENGINE_WEB_AUDIOWORKLET_REPIN override — pin matched, no shipped file changed. bash scripts/check-web-audioworklet.sh <dir> — PASS.

Seen, not done

json-syntax hard-codes hashbrown's default ahash hasher; there is no Cargo feature or
call-site override that removes the lazily-allocated statics. A static-free (or eagerly
initialized) hasher would need an upstream change to json-syntax or a fork, which is out of
scope for a test-only CI fix. #391 already tracks a separate upstream json-syntax defect (the
empty-object CodeMap volume bug from #387/#390) and shows the shape a follow-up like this would
take (upstream PR, optional [patch] ruling against docs/REALTIME_DEPENDENCY_POLICY.md,
#[ignore]d regression test until it ships); no equivalent tracking issue exists yet for the
ahash hasher statics this PR works around in-tree.

Skipped

  • No lineage re-pin, per the Gate output above: nothing in the AudioWorklet closure changed.
  • No behavioral or shipped-code change; this PR touches only a test file and one documentation
    sentence.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EwL1uTcxsmopHtamG6bMko


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…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 tamashi095 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant