mailbox S0: dual-glue flag, beacon counters, N2 red spec

Doc 17 step S0 scaffolding: WX_SCHEDULER=1 injector path with an
observation-only asyncify-scheduler.js skeleton (legacy shim stays
authoritative until S2), guard-beacon extraction with occurrence
recovery for rate-limited beacons, and the fixme'd N2 ordering spec
(add-then-move probe; un-fixme at S1).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TfxKn5utcntBSnxz4ZnYKs
This commit is contained in:
Gergő Törcsvári 2026-08-05 13:43:23 +02:00
commit 94ae4a8a41
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
5 changed files with 354 additions and 1 deletions

View file

@ -5,6 +5,8 @@
# The actual JavaScript that gets injected lives in readable, standalone files in
# scripts/common/shims/ (not inline heredocs):
# - handlesleep.js nested-Asyncify handleSleep currData save/restore (#9153)
# - asyncify-scheduler.js WX_SCHEDULER=1 builds only: the mailbox/scheduler
# (docs/features/async/17; S0 = observation-only skeleton)
# - diagnostics.js optional logging-only instrumentation (see SHIM_DIAGNOSTICS)
#
# Native wasm-EH is the only build mode, so the .js has no invoke_* wrappers / dynCall_<sig> call
@ -17,6 +19,7 @@
# Usage:
# inject-dyncall-shims.sh <pcbnew.js>
# SHIM_DIAGNOSTICS=1 inject-dyncall-shims.sh <pcbnew.js> # also inject diagnostics.js
# WX_SCHEDULER=1 inject-dyncall-shims.sh <pcbnew.js> # also inject the scheduler (dual-glue variant)
set -e
@ -32,7 +35,7 @@ if [ -z "$JS_FILE" ] || [ ! -f "$JS_FILE" ]; then
echo "Usage: $0 <path/to/pcbnew.js>"
exit 1
fi
for f in handlesleep.js diagnostics.js; do
for f in handlesleep.js asyncify-scheduler.js diagnostics.js; do
if [ ! -f "$SHIM_DIR/$f" ]; then
echo "Error: missing shim source $SHIM_DIR/$f"
exit 1
@ -99,6 +102,24 @@ else
fi
fi
# --- 3e. Mailbox/scheduler (WX_SCHEDULER=1 dual-glue variant) ------------------
# docs/features/async/17-mailbox-scheduler-plan.md, step S0. Appended AFTER the
# handleSleep shim: the legacy shim stays authoritative until S2, when the
# scheduler takes ownership of currData and this ordering flips. Idempotent via
# the __wxSchedulerInstalled marker. Default OFF — the legacy build is the
# shippable fallback until S5.
if [ "${WX_SCHEDULER:-0}" = "1" ]; then
if grep -q '__wxSchedulerInstalled' "$JS_FILE"; then
echo "asyncify-scheduler already present - skipping"
else
echo "" >> "$JS_FILE"
cat "$SHIM_DIR/asyncify-scheduler.js" >> "$JS_FILE"
echo "Injected asyncify-scheduler (WX_SCHEDULER=1 dual-glue variant)"
fi
else
echo "scheduler disabled (set WX_SCHEDULER=1 for the dual-glue variant)"
fi
# --- 3b. embind dynCall fallback (dynCallLegacy -> wasmExports) ----------------
# embind's generic caller (getDynCaller) routes through dynCallLegacy, which only
# reads Module["dynCall_<sig>"]. But the DYNCALLS=1 trampolines are wasm EXPORTS,

View file

@ -0,0 +1,42 @@
// === AsyncifyScheduler (S0 scaffolding — observation-only) ===
// docs/features/async/17-mailbox-scheduler-plan.md · injected only on WX_SCHEDULER=1 builds.
//
// S0 contract: this file changes NO runtime behavior. It claims the namespace, the
// registry data structures, and the build marker so (a) the dual-glue build variant
// exists and can run the full suite, and (b) tests can detect which runtime they're on.
// S2 turns this into the sole owner of Asyncify.currData/state (doc 13 §1): the four
// hooks (handleSleep wrap, fiber-swap tracking, trampoline ownership, deferred drain)
// land there, gated on the N1 single-writer tripwire. Until then the legacy
// handlesleep.js shim (injected just above) stays authoritative.
if (typeof Asyncify !== "undefined" && !globalThis.__wxSchedulerInstalled) {
globalThis.__wxSchedulerInstalled = true;
Asyncify.__schedulerBuild = 1;
var AsyncifyScheduler = {
// ctx = { id, kind: 'main'|'modal'|'nested'|'coroutine'|'sleep',
// buffer, status: 'running'|'parked'|'ready', wakeReason, result }
contexts: new Map(),
readyQueue: [],
running: null,
transitionRunning: false,
trampolineRunning: false,
// S2 fills these in. They throw today so a premature caller is loud, not silent —
// nothing in an S0 build calls them.
park: function () { throw new Error("[wx-scheduler] park(): not implemented until S2"); },
resume: function () { throw new Error("[wx-scheduler] resume(): not implemented until S2"); },
drain: function () { throw new Error("[wx-scheduler] drain(): not implemented until S2"); },
state: function () {
return "[wx-scheduler] build=1 impl=S0-observation-only"
+ " contexts=" + this.contexts.size
+ " ready=" + this.readyQueue.length
+ " running=" + this.running
+ " transition=" + this.transitionRunning;
},
};
globalThis.__wxScheduler = AsyncifyScheduler;
console.log("[wx-scheduler] scaffolding installed (S0, observation-only)");
}
// === End AsyncifyScheduler ===