You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
perf(runtime): drop asyncify — sandbox on the sync QuickJS variant (ADR-0102 D2, #3296)
Phase 2 of #3275. Switch the QuickJS sandbox from the asyncify build
(newAsyncContext) to the already-installed sync release variant
(newQuickJSWASMModule().newContext()), keeping one physically isolated WASM
module per invocation (D2/D4). Asyncify's only justification — suspending the
WASM stack across a host call — disappeared with the #1867 deferred-promise +
pump redesign, so nothing depended on it. Wins: smaller binary, faster
compile/instantiate + per-instruction, and removal of the suspended-stack
failure class.
- evalCodeAsync → evalCode; QuickJSAsyncContext → QuickJSContext throughout.
- Disposal: the sync QuickJSWASMModule has no dispose() — dispose the context
(frees its runtime), drop `mod` so GC reclaims the WASM instance + linear
memory. Isolation invariant preserved.
- Latent-leak fix surfaced by the stricter sync teardown: host ctx.api calls
hand the VM a vm.newPromise() deferred that was never dispose()d (the
newPromise contract requires it). Asyncify tolerated the leak; the sync build's
JS_FreeRuntime aborted ("Assertion failed: list_empty") when a context was torn
down with a pending never-settled host call (the timeout path). Deferreds are
now tracked and disposed before context teardown.
- New RSS soak test guards that per-invocation modules don't ratchet RSS (GC
reclaims them).
- ADR-0102 D2 updated with the verified disposal model.
58 sandbox + 574 runtime tests green; RSS soak green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011iJLjqToxNv1aYP3syQtRp
Copy file name to clipboardExpand all lines: docs/adr/0102-sandbox-cpu-budget-and-engine-variant.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -48,7 +48,7 @@ Settled (Phase 1): the ceiling is both a constructor option (`wallCeilingMs`, ne
48
48
Switch `newAsyncContext()` → `newQuickJSWASMModule().newContext()` (the sync release variant `@jitl/quickjs-wasmfile-release-sync` is already installed) and `evalCodeAsync` → `evalCode`. The sync surface is otherwise identical (`newPromise`, `executePendingJobs`, `setInterruptHandler`, `setMemoryLimit`, `setMaxStackSize`).
49
49
50
50
-**Invariant (normative): one fresh, isolated WASM module per invocation.**`newQuickJSWASMModule()` creates a new WebAssembly instance with its own linear memory; nothing may downgrade this to runtime- or context-level sharing (see D4).
51
-
- Disposal transfers from the context to the pair: disposecontext, then module — the module owns the linear memory.
51
+
- Disposal (verified against 0.32): `QuickJSWASMModule.newContext()` docs "the runtime will be disposed when the context is disposed", and — unlike the async convenience — the sync `QuickJSWASMModule` exposes **no `dispose()`**. So we `dispose()` the context (frees its runtime + QuickJS allocations), then drop the module reference; the module's WebAssembly instance + linear memory are reclaimed by **GC**. This is the library's intended sync pattern (only `newContext()` on a *shared* module shares memory), and it preserves the per-invocation-isolation invariant — but reclamation is GC-timed rather than the async path's explicit module dispose. **This is the one behavioural delta of D2 and MUST be gated by the RSS soak** (see Verification): burst hook-storm load must not ratchet RSS.
52
52
- Wins: no asyncify state machine on every call (per-instruction speedup), smaller binary, faster compile/instantiate, and removal of an entire class of suspended-stack failure modes the current code only avoids by convention.
53
53
54
54
### D3 — Compile the WASM once; instantiate per invocation (deferrable)
0 commit comments