ReviewOS

zig-utils/zig-js

Embedding: poll VM-wide termination at execution checkpoints

#909
Closed chris opened this 11 days ago ·
11 days ago

Parents: #495, #493. Related: #908, #896, #474.

Measured boundary

During #908 validation on macOS arm64 with Zig 0.17.0-dev.1441, an independent C host linked to the current Debug private/public archive:

  1. Creates a JSGlobalContextCreate realm and obtains its VM.
  2. Calls JSC__VM__notifyNeedTermination(vm).
  3. Evaluates let total=0; for(let i=0;i<10000;i++) total+=i; total.

Observed output:

requested=1 result=value exception=null number=49995000

The request is set, but the script crosses many engine checkpoints and returns success. This is a measured lost termination boundary, not a sanitizer race report. The tested candidate is based on 8d7a3b7a with #908 exception-slot ownership changes; the termination/checkpoint sources were unchanged. No introducing revision has been attributed.

Source finding

  • privateExecutionWatchdog publishes group.termination_requested and calls group.primary.requestTermination().
  • JSGlobalContextCreateInGroup installs the shared termination_request_flag in each sibling.
  • Each sibling Interpreter's stop_flag points to its own Context's stop flag/teardown_stop.
  • Interpreter.serviceVmTraps writes the shared request when a watchdog or shell trap fires, but never reads an already-published VM termination request. The tree walker and VM poll the realm-local stop flag.

The Home native fixture's watchdog test only requires an exception plus the VM's request bit. It never checks that the exception is termination rather than RangeError: evaluation step budget exceeded. The TSan fixture spent minutes inside the unbounded VM loop while watchdog threads slept, then reported success. A sampled stack is consistent with the fallback budget ending the loop; the standalone probe above supplies the direct functional evidence.

Required work

  • Poll VM-owned termination consistently at existing tree-walker, VM, quickened, and native checkpoint boundaries without conflating it with permanent Context teardown.
  • Preserve clear/rearm semantics, sibling isolation for realm-only teardown, VM-wide termination, and first pending exception ownership.
  • Make the native watchdog witness distinguish termination from budget exhaustion; do not lower the workload/budget, add retries/sleeps, or accept a different exception as evidence.
  • Add deterministic already-requested and asynchronous host-request witnesses, including a cleared request and sibling realms, across relevant execution tiers.
  • Verify Debug, ReleaseSafe, suppression-free TSan, private ABI fixtures, and full units.
11 days ago

Implemented on main by 0e3e248, with the Home fixture initialization corrected by 20b15b5.

The shared VM termination request is now polled by the common trap service used by tree-walker, bytecode, quickened, and native checkpoints. It remains distinct from permanent Context teardown. The tests cover already-published and asynchronous requests, sibling realms, first-pending exception behavior, clear/rearm, realm reuse, and the exact Error: worker terminated witness so a step-budget RangeError cannot satisfy the fixture.

Validated at ffc4e58:

  • focused private VM termination tests: 3/3 passed in Debug, ReleaseSafe, and suppression-free TSan
  • public/private ABI audits: 117/117 public declarations complete; Home 447/447 private symbols; Bun 437/437 private symbols
  • Home private ABI runtime fixture: 392/392 symbols linked and runtime matrix passed in Debug, ReleaseSafe, and TSan
  • private global lifecycle fixture passed in Debug, ReleaseSafe, and TSan
  • dedicated JIT, VM, and concurrency suites passed
  • full sharded unit suite: 2,432 passed, 1 skipped, 0 failed, 0 leaked
  • deterministic threadfuzz default profile: 400 programs, 0 failures
  • docs link check: 335 files, all links resolved; docs build: 50 pages, manifest matched

Sign in to comment on this issue.