From a483566ff2908a4730b869e3be8e2259487627da Mon Sep 17 00:00:00 2001 From: BL Date: Fri, 4 Sep 2026 19:16:18 +0000 Subject: [PATCH] CI: warm ahash process statics before the capi lifecycle allocator oracle 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 Claude-Session: https://claude.ai/code/session_01EwL1uTcxsmopHtamG6bMko --- crates/capi/tests/resource_lifecycle.rs | 16 ++++++++++++++++ docs/REALTIME_DEPENDENCY_POLICY.md | 7 ++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/crates/capi/tests/resource_lifecycle.rs b/crates/capi/tests/resource_lifecycle.rs index 24ff7654b..0e0e35a33 100644 --- a/crates/capi/tests/resource_lifecycle.rs +++ b/crates/capi/tests/resource_lifecycle.rs @@ -130,7 +130,23 @@ fn snapshot() -> Snapshot { } } +/// Initialize the process-lifetime statics that the JSON frontend's dependencies create lazily, +/// so the first observed window is not charged for them. +/// +/// `json-syntax` 0.12.5 indexes every object 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`, and the +/// `SEEDS` array: 8 + 16 + 64 bytes) that live until process exit and belong to no capi owner. +/// ahash's `build.rs` forces `runtime-rng` on every hosted target, so no Cargo feature removes +/// them. They land on whichever thread parses the first object, which under the parallel +/// harness is a race between this file's tests; parsing a trivial object here makes every +/// window start after that initialization on this thread, independent of sibling scheduling. +fn warm_process_lifetime_statics() { + let _ = session::parse_session_json(r#"{"warm":0}"#); +} + fn begin() { + warm_process_lifetime_statics(); // Initialize the thread-local keys before observation is armed. ACTIVE.set(false); ALLOCATIONS.set(0); diff --git a/docs/REALTIME_DEPENDENCY_POLICY.md b/docs/REALTIME_DEPENDENCY_POLICY.md index dc3d173b6..505ca219d 100644 --- a/docs/REALTIME_DEPENDENCY_POLICY.md +++ b/docs/REALTIME_DEPENDENCY_POLICY.md @@ -343,7 +343,12 @@ Parser allocation and diagnostic formatting are expected control-plane behavior. returns typed diagnostics; arithmetic and configured-cap preflight runs before the canonical string, model clone, normalized indexes, or downstream plan work. JSON string and duplicate-key fixtures, the strict unknown-key matrix, target compilation, and parser/compiler fuzz targets are -the compatibility and failure-mode evidence for this dependency choice. +the compatibility and failure-mode evidence for this dependency choice. `json-syntax`'s object +indexing goes through `hashbrown` 0.12's default hasher, `ahash` 0.7's `RandomState`, which on its +first construction in a process lazily allocates 88 bytes of process-lifetime heap and seeds from +OS entropy per process -- harmless to every shipped cap (they are explicit row sums), but relevant +to any exact allocator oracle that arms across the first object parse in a process (see +`crates/capi/tests/resource_lifecycle.rs`). The earlier issue-004 cross-target archive sizes are historical measurements for the retired parser stack and are not projected onto the JSON implementation. Current size and allocation