From 603e2608858741c329c2ecb26b0bcb97427dcbe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20T=C3=B6rcsv=C3=A1ri?= Date: Wed, 5 Aug 2026 15:55:16 +0200 Subject: [PATCH] =?UTF-8?q?mailbox=20S2:=20scheduler=20core=20=E2=80=94=20?= =?UTF-8?q?deferred=20wakes=20+=20N1=20single-writer=20tripwire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit asyncify-scheduler.js REPLACES handlesleep.js on WX_SCHEDULER=1 builds (injector either-or): ports capture/restore, fiber consume-once/ quarantine guard, wake-window flags, recorder, trampoline heal — and adds deferred wakes (a wake mid-transition queues and drains from a clean macrotask) plus the N1 currData accessor (pure-JS writes need scheduler authorization; strict mode throws; meta-tested). Gates: races 9/9 with NO legacy shim (subsumption), coroutine 39/39, wx-chromium 30/30, kicad trio 3/3 on the C-lane build. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01TfxKn5utcntBSnxz4ZnYKs --- .../async/17-mailbox-scheduler-plan.md | 18 + scripts/common/inject-dyncall-shims.sh | 87 ++-- scripts/common/shims/asyncify-scheduler.js | 470 +++++++++++++++--- .../asyncify/asyncify-races-scheduler.spec.ts | 122 +++++ 4 files changed, 592 insertions(+), 105 deletions(-) create mode 100644 tests/asyncify/asyncify-races-scheduler.spec.ts diff --git a/docs/features/async/17-mailbox-scheduler-plan.md b/docs/features/async/17-mailbox-scheduler-plan.md index 5ee1390..ba48c2e 100644 --- a/docs/features/async/17-mailbox-scheduler-plan.md +++ b/docs/features/async/17-mailbox-scheduler-plan.md @@ -188,6 +188,24 @@ rollback = the `WX_SCHEDULER=0` build + a per-step tag. `_asyncify_stop_rewind`/`maybeStopUnwind`, never JS `finally`), fiber tracking at `emscripten_fiber_swap` (`fiber+20` buffers), trampoline ownership. **Gate:** races battery green **with the legacy shim ablated**; N1/N4/N5 green; libcontext refusal beacons ≈ 0. + > **Work log 2026-08-05 — S2 core LANDED and gated.** The scheduler shim now REPLACES + > handlesleep.js on WX_SCHEDULER=1 builds (injector either-or; the S1 "append-after" + > ordering is gone). Ported name-identical: capture/restore, wake-window flags + > (`__wakingRoot` is read by libcontext EM_JS!), consume-once/quarantine fiber guard, + > counters, flight recorder, trampoline heal (external catch-reset wrap). NEW: + > **deferred wakes** — a sleep wake arriving mid-transition (state≠Normal or trampoline + > live) queues and drains from a clean macrotask (the aliased-wake class is now + > structural, not detective); **N1 accessor** — `Asyncify.currData` is a property with a + > single-writer tripwire (pure-JS writes need scheduler authorization; wasm-frame writes + > pass; strict mode throws), meta-tested by introducing a stray. + > **Gates all green:** asyncify-firefox 9/9 (7 races + new N1 meta + N4 books — on glue + > with NO legacy shim, so the redundancy pins are now subsumption pins), coroutine 39/39, + > wx-chromium 30/30, kicad trio 3/3 on the C-lane build. Injector legacy path verified + > unchanged. **Deliberately left for S3:** the formal ctx-Map registry + park/resume + > methods (stubs that throw), wasm-side ProcessEvents; **open:** N5 flood spec, both-EH + > matrix (CI, deferred). Build-system note: `build-wasm-test.sh` only re-injects freshly + > relinked apps — a variant flip without C changes needs the strip+reinject converter + > (one-shot python in the work log commit) or a clean build. - **S3 · Root context + wasm-side ProcessEvents (≈1 wk).** The tick resumes the root context which calls `ProcessEvents` on the wasm side — the `await ccall(...,{async:true})` boundary (#13302) is removed. The root context never awaits JS (13 §6b). **Gate:** net green with diff --git a/scripts/common/inject-dyncall-shims.sh b/scripts/common/inject-dyncall-shims.sh index 4377692..7a26e76 100755 --- a/scripts/common/inject-dyncall-shims.sh +++ b/scripts/common/inject-dyncall-shims.sh @@ -72,54 +72,53 @@ apply_fix 'var iterFunc = (() => {});' 'var iterFunc = () => wasmExports["dynCal apply_fix '(a1 => {})(userData);' 'wasmExports["dynCall_vi"](entryPoint, userData);' "fiber entry callback(s) (wasmExports.dynCall_vi)" echo "Total: Fixed $TOTAL_FIXED empty callback(s)" -# --- 3. Nested-Asyncify handleSleep fix --------------------------------------- -# Injected after Emscripten's fiber glue (the _emscripten_fiber_swap.isAsync marker). -# SHIM_DISABLE_HANDLESLEEP=1 skips it: used by the asyncify-races red-green harness -# to keep the historical "sleep buffer clobbered by fiber swap" crash reproducible. -if [ "${SHIM_DISABLE_HANDLESLEEP:-0}" = "1" ]; then +# --- 3. Asyncify shim: legacy handleSleep fix OR the S2 scheduler -------------- +# Injected after Emscripten's fiber glue (the _emscripten_fiber_swap.isAsync marker), +# or at EOF for non-fiber apps (a plain wx app still needs the currData machinery: +# without it a rewind resuming through a fresh wasm re-entry hits +# _asyncify_start_rewind(null) -> "memory access out of bounds"). +# +# WX_SCHEDULER=1 (doc 17 S2): asyncify-scheduler.js REPLACES handlesleep.js — it +# subsumes the capture/restore, fiber guard, and trampoline heal, and adds the +# deferred-wake drain + N1 single-writer tripwire. Injecting BOTH would +# double-manage the wake path (the scheduler refuses to install its core then). +# +# SHIM_DISABLE_HANDLESLEEP=1 skips the legacy shim: the asyncify-races red-green +# harness uses it to keep the historical "sleep buffer clobbered by fiber swap" +# crash reproducible. On WX_SCHEDULER=1 builds the variant still gets the +# scheduler — the ablation pins become scheduler-subsumption pins (doc 17 §3c). +inject_shim_at_marker() { #