ReviewOS

zig-utils/zig-js

Runtime: coordinate GC, JIT, Wasm, and worker CPU budgets

#502
Open chris opened this 9 days ago ·
9 days ago

Parent: #459 Depends on: #476, #481, #482, #490 Related: #477, #478, #492, #503

Goal

Coordinate GC helpers, JIT/Wasm compilation, JavaScript Threads, Workers, waiters, and host tasks under explicit bounded CPU, memory, and latency budgets.

Problem

GC, native compilation, Wasm work, shared-realm Threads, isolated Workers, and benchmark workers make separate concurrency decisions. As parallel tiers land, independent pools/helpers can oversubscribe cores, steal latency from mutators, inflate RSS, or create priority inversions even when each subsystem is locally correct.

Scope

  • Inventory every engine-created thread/helper/pool, its ownership, lifecycle, blocking behavior, memory/scratch, affinity, priority, and participation in GC/JIT teardown.
  • Add a per-VM/process resource coordinator with configurable and automatically bounded CPU slots, compilation queue, GC helper count, worker admission, scratch/memory pressure, and shutdown.
  • Prioritize mutator progress, foreground latency, termination/debugger work, GC safety, and bounded memory over speculative compilation.
  • Prevent GC/JIT/Wasm helpers from deadlocking or starving when JavaScript threads block in Lock/Condition/Atomics.wait, host callbacks, join, or module loading.
  • Apply backpressure/cancellation to stale compilation and queued internal work with exact ownership and no partially published artifact.
  • Expose embedder controls and machine-readable scheduling decisions without making host thread handles cross-affine.
  • Benchmark dedicated and oversubscribed hosts, small/large core counts, mixed subsystems, and memory pressure.

No-workaround rules

  • No hardcoded benchmark core count, sleep/yield loop, hidden GIL, unbounded pool/queue, OS-priority abuse, dropped task, canceled required GC, or throughput result that silently starves latency work.

Acceptance

  • A machine-readable thread/resource inventory has zero unclassified engine-created concurrency. Completed by #983, #984, and #985; the fail-closed audit reports 6 production call sites across 6 resource classes and 143 reviewed test-only direct spawns.
  • Deterministic scheduler-pressure tests cover GC, compilation, Workers/Threads, Atomics/waits, modules, termination, OOM, and teardown.
  • #461/#492 show bounded queues/threads/RSS, stable tail latency, and no throughput collapse under oversubscription.
  • Single-thread/small-core workloads do not pay material coordination overhead.
  • No-GIL, TSan, lifecycle/mid-GC fuzz, artifact lifetime, and Wasm threads gates pass.

Foundation

Current pooling/scheduler guidance lives in docs/threads/production-readiness.md and docs/threads/limits.md; subsystem work is tracked by #476/#481/#482/#490.

9 days ago

First acceptance foundation landed through #978 in 40b1b93. A versioned machine-readable inventory now covers every production engine-created OS thread (six call sites/resource classes), including ownership, admission, stack, blocking, memory, and shutdown. A token-aware build audit fails on unclassified typed/direct creation and proves all 141 remaining direct src/ spawns are test-only. JIT and Wasm compilation are recorded as synchronous. This does not yet satisfy #502: all six resources remain explicitly uncoordinated, and shared slots/backpressure/cancellation/telemetry are next.

9 days ago

#979 is complete in d68cf1c. The #978 typed boundary now supplies an exact public process snapshot for each resource class, including in-flight spawn accounting, live/peak concurrency, spawn failures, and configured native-stack reservations. Snapshot reads are coherent across concurrent start/exit, and focused TSan plus full units are clean. This establishes telemetry for admission and pressure decisions; shared CPU slots, blocked/runnable transitions, backpressure/cancellation, and embedder policy controls remain.

9 days ago

#980 is complete on main in cab1dd6. The runtime now has exact, process-wide admission and backpressure for live OS-thread count and configured stack bytes across all six audited resource kinds, with coherent schema-v2 telemetry and unlimited defaults. Focused normal/TSan pressure checks and the full 2,496-test unit gate pass. Remaining scheduler work includes runnable CPU-slot limits, blocked/runnable transition accounting, and stale-task cancellation.

9 days ago

#981 is complete on main in 75a93eb. All six engine-owned OS-thread classes now expose coherent schema-v3 runnable/blocked state, nested transition totals, and the invariant live = runnable + blocked across agent, Worker, shared-realm Thread, GIL/task, timer, collector, teardown, and watchdog parks. Normal/TSan pressure tests and the full 2,500-test unit gate pass. This supplies the state contract needed for shared runnable CPU slots; enforcement, priority policy, queued internal work, and stale-task cancellation remain.

9 days ago

#982 is complete in d2e6383 on main. All six typed runtime thread classes now share a condition-backed runnable CPU-slot budget with mutex-safe release/reacquire around engine blocking points. Schema-v4 telemetry exposes slot occupancy and pressure, and full units plus deterministic threadfuzz pass with zero failures/leaks.

The remaining #502 work is automatic host sizing, priority policy, compilation queues, cancellation, and memory pressure integration.

9 days ago

#983 is complete in 7bfffb8 on main. Runnable CPU slots are now automatically bounded to max(1, logical CPUs - 1) by default, with one host/embedder lane reserved and exact nullable fixed/zero/unlimited overrides. Schema-v5 telemetry exposes detected, configured, reserved, and effective capacity. Full units, focused TSan, and deterministic threadfuzz pass with zero failures/leaks.

Remaining #502 work: priority policy, compilation queues and stale-work cancellation, memory-pressure integration, and the dedicated/oversubscribed host evidence gates.

9 days ago

#984 is complete in 7c23eb6 on main. All six typed resources now use audited safety/foreground/background classes, allocation-free FIFO waiter queues, and a starvation-bounded 4:2:1 grant cycle. Schema-v6 exposes per-class queue pressure and decisions. Full units, focused TSan, and deterministic threadfuzz pass with zero failures/leaks.

Remaining #502 work: compilation queues and stale-work cancellation, memory-pressure integration, and dedicated/oversubscribed host evidence gates.

9 days ago

Opened #985 for the next #502 slice: coordinate synchronous JIT/Wasm compiler CPU across typed and embedder host threads, with a clean measure-first benchmark and internal-work telemetry. Current production compilation has no queue: claim, compile, adopt, and generation-checked publication are synchronous, and typed JavaScript Threads already compile inside their runnable slot. #985 therefore keeps that exact lifetime and will not invent an asynchronous cancellation path unless the predeclared evidence required by #481 justifies background compilation.

9 days ago

#985 is complete and closed.

The process-wide coordinator now accounts for synchronous native and Wasm compilation as background internal work. Typed engine threads reuse their runnable slot; untyped hosts share one automatic reservation and the bounded weighted queue. The final 9-sample evidence shows no host-width wall regression (-1.7% at 11 lanes) and exact 11-way peak work under 22-lane oversubscription, with 1,372/1,496 median attempts queued. It therefore does not justify adding an asynchronous artifact queue or cancellation lifetime.

The inventory acceptance item is now checked: the fail-closed audit covers all 6 production call sites/resource classes plus 143 reviewed test-only direct spawns.

The remaining #502 work is cross-subsystem scratch/memory-pressure accounting and the combined pressure evidence required by #461/#492. The unrelated TSan deep-source witness is tracked as #986.

9 days ago

Child #1027 is complete for the cross-subsystem scratch-memory part of this coordinator.

Landed directly on main:

  • b4d773c2 — process-wide fail-closed scratch admission and schema-v8 telemetry
  • bfd6700a — baseline/optimizing JIT temporary compilation accounting with retryable denial
  • 3b55e737 — Wasm decode/validation temporary accounting with retained-module ownership preserved
  • f149135f, 7b1ba792, 24d7ff72, d55b4456 — paired exact-parent evidence tooling and publication
  • 8794ccc5 — exact resize/remap/nesting/release/re-admission/teardown accounting coverage

Verification includes focused normal and unsuppressed-TSan GC/JIT/Wasm accounting tests, deterministic no-GIL threadfuzz verify (8/8 normal, 4/4 TSan), the packed wg-1.0 suite (18,795 pass, 0 fail, 475 declared skip, 0 crash), and the full unit gate (2,567 pass, 1 skip, 0 fail, 0 leak).

The exact-parent report covers 1- and 20-lane runs with exact checksums, reconciled scratch bytes, teardown zero, RSS, latency, and throughput. It makes no speed claim where timing variance is high.

This does not close #502: CPU scheduling, workers/threads, and the broader oversubscription acceptance remain. The generic lifecycle profile's pre-existing waitAsync/notify failure is isolated in #1028; candidate and exact parent fail the same four seeds.

9 days ago

Closed #1028 in 32b0611. Finite property-backed Atomics.waitAsync now uses the shared runnable scheduler blocking contract and a sleepable realm progress edge, so oversubscribed workers release their slots and notification wakes them promptly. The deterministic one-slot regression passes normally and under TSan; lifecycle seeds 1027-1030 pass 224/224 in Debug, ReleaseSafe, and unsuppressed TSan; full units pass 2,568 with one skip.

9 days ago

Focused shutdown child #1032 is complete in ddad1d55.

A Worker queued before its initial runnable slot could not observe terminate(), so an embedder-set zero runnable limit made join() wait until capacity was restored. Script and module Workers now carry a caller-owned start-cancellation edge. Hard termination removes any queued position in O(1) under the coordinator mutex, balances exact live/blocked/stack/waiter accounting, closes terminal channels/inspector state on the spawned thread, and never evaluates source or loads the module graph. Grant-versus-cancel has one mutex-serialized winner; graceful close retains drain semantics.

Schema-v9 telemetry separates pre-entry cancellations from grants. The fail-closed inventory recognizes both typed spawn APIs and reports 6 production sites plus 151 reviewed test-only direct spawns.

Evidence: head/middle/tail removal and 32 grant races pass normally and under unsuppressed TSan; real script/module cancellation passes normally and under TSan; full units pass 2,571 with one platform skip, zero failures/leaks; production build, inventory audit, 378-file link audit, and the 15/15-step, 19/19-validator benchmark tooling gate pass. Test262 flips were not measured because the corpus is unavailable in this checkout.

This advances #502's deterministic Worker/termination/teardown pressure coverage. The broader mixed GC/compilation/Threads/modules/OOM pressure matrix and oversubscription evidence remain open.

9 days ago

#1033 landed on main in f26b968.

Shared-realm JavaScript Thread teardown now cancels pre-entry starts that are still queued behind the process-wide runnable limit. Cancellation releases active-interpreter and microtask-transfer reservations, clears entry roots, cancels prepared async-join settlements, and publishes done/joins_settled/exited without running the entry callable. Starts that already won a slot retain the existing teardown stop and checkpoint path.

Evidence: focused normal and unsuppressed TSan tests pass; the full unit suite reports 2,573 passed, 1 skipped, 0 failed, and 0 leaked; the build, runtime-thread audit, and 378-file docs link check pass. This advances the Thread/Context teardown portion of #502; the broader mixed-resource acceptance work remains open.

9 days ago

#1034 landed on main in 1cb677d.

Concurrent-GC finish boundaries now cancel a marker that is still queued before its initial safety slot, join the OS thread, and complete the active mark through the existing synchronous path. Already-running markers retain their stop/join/finish order. This removes the zero-capacity scheduler cycle between a Context finish boundary and a pre-entry marker without raising capacity or abandoning GC work.

Evidence: the bounded pre-fix reproduction failed until capacity restoration; the post-fix reproduction finishes while capacity remains zero; eight repeated queued cycles and one running cycle balance exact coordinator accounting; focused and broader concurrent-GC normal/unsuppressed-TSan tests pass; full units report 2,575 passed, 1 skipped, 0 failed, and 0 leaked. The build, 153-spawn runtime audit, and 378-file link gate pass.

This advances #502's deterministic GC/teardown pressure coverage. Test262 evidence remains unavailable in this checkout, and the broader mixed-resource and oversubscription acceptance items remain open.

9 days ago

#1035 landed on main in d9d1616.

Private VM group teardown now cancels an execution watchdog that is still queued before its initial safety slot, then joins it before reclaiming group state. Already-running watchdogs retain the existing deadline, termination, blocked-sleep, and stop/join behavior. The pinned public/private ABI surface is unchanged.

Evidence: the pre-fix bounded group-release reproduction blocked until capacity restoration; post-fix it completes while capacity remains zero. Eight repeated queued groups and one running watchdog balance exact coordinator accounting. All watchdog tests pass normally and under unsuppressed TSan; the Home 392-symbol runtime fixture and every C/Home/Bun/JSType ABI audit pass; full units report 2,577 passed, 1 skipped, 0 failed, and 0 leaked. The build, 154-spawn runtime audit, and 378-file link gate pass.

This advances #502's deterministic termination/teardown pressure coverage. Test262 evidence remains unavailable in this checkout, and the broader mixed-resource and oversubscription acceptance items remain open.

9 days ago

#1036 landed on main in 1424aa2.

The test262 host now cancels all pre-entry Agent starts before joining any handle during group reset. Canceled starts publish terminal group and waiter notifications without executing source; granted/running agents retain broadcast, report, Atomics, completion, and join behavior. This closes the last uncancelable pre-entry lifecycle among the six audited production typed thread sites.

Evidence: the pre-fix bounded reset reproduction blocked until capacity restoration; post-fix it completes while capacity stays zero. Eight queued agents execute source zero times and balance exact coordinator accounting; running broadcast-park and completion cases pass. Focused normal and unsuppressed TSan tests pass; the final full suite reports 2,579 passed, 1 skipped, 0 failed, and 0 leaked. The build, 155-spawn runtime audit, and 378-file link gate pass.

This advances #502's deterministic Atomics/termination/teardown pressure coverage. The pinned test262 corpus is unavailable in this checkout, and the broader combined mixed-resource and oversubscription acceptance items remain open.

9 days ago

#1037 landed on main in be7ddc6 and completes #502's deterministic scheduler-pressure acceptance item.

The new coordinator gate overlaps all six typed resource classes, both compilation work classes, all three scratch domains at their exact shared limit, a policy denial/readmission, foreground/background cancellation, a real blocked-marker handoff, safety priority, and background FIFO between host work and a Worker. Every current live/runnable/blocked/stack/waiter/work/scratch counter must reconcile exactly to baseline.

Together with production-path coverage from #1027 (compilation/Wasm scratch and OOM), #1028 (Atomics wait), #1032 (script/module Workers and termination), #1033 (shared-realm Threads), #1034 (concurrent GC), #1035 (watchdog termination), and #1036 (test262 agents), this covers every subsystem named by the acceptance line. The checkbox is now marked complete.

Evidence: 10/10 fresh normal and 10/10 unsuppressed-TSan focused runs pass; full units report 2,580 passed, 1 skipped, 0 failed, and 0 leaked; the build, 156-spawn audit, and 378-file link gate pass. Remaining #502 work is the #461/#492 bounded long-run evidence, small-core coordination-overhead evidence, and the complete no-GIL/fuzz/Wasm gate family.

9 days ago

Additional bounded-scheduler liveness coverage landed in 75395544 (#1039). Hosted lifecycle/mid-GC timeouts were traced to stress programs that occupied every runnable slot while waiting for queued peers. Cross-resource rendezvous now park through the coordinator, termination Workers preserve observable progress before each park, and a one-slot boundedlifecycle replay covers seven affected families. Normal, unsuppressed TSan, lifecycle, and mid-GC validations all pass with no test262 flips.

9 days ago

Resource-coordination follow-up completed:

  • #1040 / e9c7c0b3: CPU-bound no-GIL interpreters now hand contended runtime slots to older queued threads at bounded tree-walker, bytecode, quickened, and native checkpoints while freezing GC roots.
  • #1041 / 20044bc3: the native global-binding cache witness now deterministically exercises an eligible external global slot.
  • #1042 / f9ed7030: the multi-age cooperative nursery witness drives its successful-collection precondition under loaded-shard rendezvous timeouts.

Final local gate: 2,583 passed, 1 skipped, 0 failed, 0 leaked in 561.9 s. The two formerly starved ReleaseSafe no-GIL corpus cases pass in 310 ms and 163 ms.

9 days ago

Scheduler follow-up 04c6c598 fixes the small-host no-GIL mutator-stop deadlock exposed by hosted shard 2: scheduler-parked interpreters now remain pinned across later realm-wide stops and publish the active generation before resume. Forced one-slot Debug, ReleaseSafe, and unsuppressed TSan runs pass both mc-life-wasm-grow-relocate.js and mc-grow-buffer-storm.js; full units are 2,585 pass / 1 skip / 0 fail / 0 leak. #1040 remains open pending the new hosted full-shard result.

9 days ago

#1040 is complete. The cooperative runnable-slot handoff landed in 04c6c598, and hosted run 37736911024 passed the scheduler-exposed corpus cases in both Debug and ReleaseSafe: mc-grow-buffer-storm.js on shard 2, plus mc-grow-s4-detach-nullvec-repro.js and mc-life-wasm-grow-relocate.js on shard 3. The change also passed the deterministic one-slot tree-walker/VM witnesses, focused unsuppressed TSan, and the 2,585-test unit gate.

Sign in to comment on this issue.