design-b D-1a: retire the legacy-shim ablation builds + redundancy pins

Doc 20 D-1 (legacy runtime deletion), first slice: races_test_noheal /
races_test_nosleepfix pinned behavior of the legacy handlesleep shim,
which is being deleted — the pins now assert properties of a runtime
that no longer exists. Drops the two Makefile.wasm link+inject variants
(SHIM_DISABLE_TRAMPOLINE_HEAL / SHIM_DISABLE_HANDLESLEEP), the
shim-redundancy pin specs in asyncify-races.spec.ts, and resolves the
tests/README.md open task. The green battery still runs every scenario
against the scheduler glue.

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

View file

@ -303,18 +303,14 @@ Button positions (relative to canvas):
## Open tasks
- **Research: are the Asyncify fiber shims still needed under native-EH?** Two
`scripts/common/inject-dyncall-shims.sh` fixes — the fiber trampoline self-heal
(§3c) and the nested-Asyncify handleSleep save/restore (§3) — were written for the
legacy-EH *park-throw* model. Under native wasm-EH the top loop is a per-frame-yield
while-loop with no park-throw, so ablating either shim
(`SHIM_DISABLE_TRAMPOLINE_HEAL` / `SHIM_DISABLE_HANDLESLEEP`, wired in
`tests/apps/Makefile.wasm`) no longer reproduces the disease it guarded — the old
red "ablation pins" in `asyncify/asyncify-races.spec.ts` have been flipped to
green "shim-redundancy pins" that now prove the native-EH path stays clean *with the
shim ablated*. **To do:** sweep the real apps (modals, nested fibers, long sleeps,
pthread pool) with each shim ablated; if all stay green, drop the shim injection and
these pins. Until proven, they stay injected (belt-and-suspenders).
- ~~Research: are the Asyncify fiber shims still needed under native-EH?~~
**Resolved at doc 20 D-1** (legacy-runtime deletion): the ablation builds
(`races_test_noheal` / `races_test_nosleepfix`) and their shim-redundancy pins in
`asyncify/asyncify-races.spec.ts` pinned a runtime that no longer exists — the
scheduler shim (`scripts/common/shims/asyncify-scheduler.js`) is the only runtime
and subsumes the handleSleep save/restore; the fiber trampoline self-heal (§3c)
remains injected unconditionally. The green battery runs every scenario against
the scheduler glue.
## Collab e2e — legacy vs v2 bundles, and repro markers

View file

@ -200,8 +200,6 @@ all: minimal_test.html \
$(S)/coroutine/coroutine_test.html \
$(S)/coroutine-nested/nested_test.html \
$(S)/asyncify-races/races_test.html \
$(S)/asyncify-races/races_test_noheal.html \
$(S)/asyncify-races/races_test_nosleepfix.html \
$(S)/coroutine-pthread/coroutine_test_wxpt.html \
$(S)/coroutine-pthread/embind_repro.html \
$(S)/coroutine-pthread/gl_repro.html \
@ -747,9 +745,9 @@ $(S)/coroutine-nested/nested_test.html: $(S)/coroutine-nested/nested_test.o $(S)
../../scripts/common/inject-dyncall-shims.sh $(basename $@).js
# Asyncify race-condition red-green harness (tests/asyncify/). Three variants of
# the same app: full shims, trampoline-heal ablated, handleSleep-fix ablated.
# The ablated builds keep the historical bugs reproducible (red) so the shim
# fixes stay pinned by tests. Same .o, three link+inject passes.
# (The trampoline-heal / handleSleep ablation variants — races_test_noheal /
# races_test_nosleepfix — were retired at doc 20 D-1 together with the legacy
# runtime they pinned.)
$(S)/asyncify-races/races_test.o: $(S)/asyncify-races/races_test.cpp $(S)/coroutine/kicad_coroutine_harness.h
$(CXX) -c $(CXXFLAGS) -I$(KICAD_ROOT)/thirdparty/libcontext -I$(S)/coroutine $< -o $@
@ -757,14 +755,6 @@ $(S)/asyncify-races/races_test.html: $(S)/asyncify-races/races_test.o $(S)/corou
$(CXX) $^ $(LDFLAGS_RACES) --pre-js $(JS) --shell-file $(HTML) -o $@
../../scripts/common/inject-dyncall-shims.sh $(basename $@).js
$(S)/asyncify-races/races_test_noheal.html: $(S)/asyncify-races/races_test.o $(S)/coroutine/libcontext.o $(WX_CORE_LIB)
$(CXX) $^ $(LDFLAGS_RACES) --pre-js $(JS) --shell-file $(HTML) -o $@
SHIM_DISABLE_TRAMPOLINE_HEAL=1 ../../scripts/common/inject-dyncall-shims.sh $(basename $@).js
$(S)/asyncify-races/races_test_nosleepfix.html: $(S)/asyncify-races/races_test.o $(S)/coroutine/libcontext.o $(WX_CORE_LIB)
$(CXX) $^ $(LDFLAGS_RACES) --pre-js $(JS) --shell-file $(HTML) -o $@
SHIM_DISABLE_HANDLESLEEP=1 ../../scripts/common/inject-dyncall-shims.sh $(basename $@).js
# Convenience targets
menu: $(S)/menu/menu_test.html
@ -836,7 +826,7 @@ $(S)/retinascale/retinascale_test.html: $(S)/retinascale/retinascale_test.o $(WX
retinascale: $(S)/retinascale/retinascale_test.html
coroutine: $(S)/coroutine/coroutine_test.html
coroutine-nested: $(S)/coroutine-nested/nested_test.html
asyncify-races: $(S)/asyncify-races/races_test.html $(S)/asyncify-races/races_test_noheal.html $(S)/asyncify-races/races_test_nosleepfix.html
asyncify-races: $(S)/asyncify-races/races_test.html
# Only delete generated app files (*_test*, *_repro*) — a bare $(S)/*/*.js would
# also delete checked-in sources like coroutine-pthread/worker_dom_stub.js.

View file

@ -411,9 +411,9 @@ private:
// ----- scenario 1: post_park_fiber_swap -------------------------------------
// The KiCad hang topology. OnInit already did a fiber swap, so the park throw
// went through the live trampoline. With the self-heal shim the guard was
// reset and this swap works; with SHIM_DISABLE_TRAMPOLINE_HEAL=1 the guard is
// stuck true, the Call() below never returns, and the watchdog fires.
// went through the live trampoline. With the self-heal shim the guard is
// reset and this swap works; historically (ablated heal, retired at doc 20
// D-1) the guard stuck true, Call() never returned, and the watchdog fired.
void Scenario_PostParkFiberSwap()
{
#ifdef __EMSCRIPTEN__
@ -608,9 +608,9 @@ private:
// ----- scenario 5: long_parked_sleep_clobbered_by_swap ------------------------
// The KiCad clipboard crash shape: a long-parked sleep crossed by complete
// fiber-swap cycles on fresh stacks. With handlesleep.js the sleep's buffer
// is restored at wakeUp; with SHIM_DISABLE_HANDLESLEEP=1 doRewind reads a
// clobbered currData -> "index out of bounds".
// fiber-swap cycles on fresh stacks. The scheduler shim restores the sleep's
// buffer at wakeUp; historically (no save/restore, ablation retired at doc 20
// D-1) doRewind read a clobbered currData -> "index out of bounds".
void Scenario_LongParkedSleep()
{
#ifdef __EMSCRIPTEN__

View file

@ -3,12 +3,11 @@ import { test, expect, tryLoadApp } from '../e2e/utils/fixtures';
// Red-green specs for the Asyncify race-condition harness
// (tests/apps/standalone/asyncify-races/races_test.cpp — see docs/features/async/).
//
// Two kinds of tests here:
// - GREEN-target tests assert the desired end state (clean pass, clean console).
// While a fix is missing they FAIL — that failing run is the recorded "red".
// - ABLATION tests run the shim-ablated builds (races_test_noheal.js /
// races_test_nosleepfix.js) and assert the historical bug REPRODUCES.
// They pin the disease so the shim fixes stay testable forever.
// GREEN-target tests assert the desired end state (clean pass, clean console).
// While a fix is missing they FAIL — that failing run is the recorded "red".
// (The shim-ablation builds and their pins were retired at doc 20 D-1 together
// with the legacy runtime they exercised; the scheduler shim is the only
// runtime, and the battery below runs every scenario against it.)
const BATTERY = [
'post_park_fiber_swap',
@ -176,78 +175,3 @@ test.describe('Asyncify races — green targets (full shims)', () => {
expect(unwindLeaks, `unwind escaped the park: ${unwindLeaks.join(' || ')}`).toHaveLength(0);
});
});
// These two were "ablation pins": they ablated a JS-shim fix (via SHIM_DISABLE_*,
// see tests/apps/Makefile.wasm) and asserted the LEGACY-EH disease came back — a
// stuck Fibers.trampoline guard (trampolineRunning=true) / a clobbered parked-sleep
// buffer. Under native wasm-EH the top loop is a per-frame-yield while-loop with NO
// park-throw (docs/features/wasm-exceptions; native-EH top-loop redesign), so the
// disease's *trigger* is gone at the root: ablating the shim no longer reproduces
// it. (The green "battery" above already runs both scenarios full-shim.)
//
// So they are flipped red->green: each now pins that the native-EH path stays clean
// EVEN with the legacy shim ablated — i.e. the scenario completes with no disease
// signature. That keeps them as the live signal for the open question below.
//
// TODO(research): are these shims still needed AT ALL under native-EH-only? The
// fiber trampoline self-heal (§3c) and the nested-Asyncify handleSleep save/restore
// (§3) in scripts/common/inject-dyncall-shims.sh were written for the legacy
// park-throw model; ablating either no longer breaks the scenario it guarded. If a
// full sweep of the real apps (modals / nested fibers / long sleeps / pthread pool)
// confirms they're dead weight under native-EH, drop the shim injection AND these
// pins. Until then they stay injected (belt-and-suspenders). Tracked in
// tests/README.md "Open tasks".
test.describe('Asyncify races — shim-redundancy pins (native-EH stays clean with the legacy shim ablated)', () => {
test('trampoline-heal ablated: native-EH post-park swap still completes (no stuck guard)', async ({
page,
testLogger,
}) => {
await page.goto(
'/standalone/asyncify-races/races_test_noheal.html#only=post_park_fiber_swap'
);
await tryLoadApp(page, 30000);
await expect
.poll(() => findSummary(testLogger.consoleLogs) ?? null, {
timeout: 45000,
message: 'post-park swap should complete even with the trampoline self-heal ablated',
})
.not.toBeNull();
const { passed, failed } = parseSummary(findSummary(testLogger.consoleLogs)!);
expect(passed).toBe(1);
expect(failed).toBe(0);
// The legacy disease signature must be ABSENT: no stuck-trampoline watchdog dump.
expect(
testLogger.consoleLogs.some((l) =>
l.includes('[ASYNCIFY_RACES] WATCHDOG post_park_fiber_swap')
),
'no stuck-trampoline watchdog should fire under native-EH'
).toBe(false);
expect(crashLines(testLogger), 'no crash signatures in console').toHaveLength(0);
});
test('handleSleep ablated: native-EH long parked sleep is not clobbered by a swap', async ({
page,
testLogger,
}) => {
await page.goto(
'/standalone/asyncify-races/races_test_nosleepfix.html#only=long_parked_sleep_clobbered_by_swap'
);
await tryLoadApp(page, 30000);
await expect
.poll(() => findSummary(testLogger.consoleLogs) ?? null, {
timeout: 45000,
message: 'long parked sleep should resolve even with the handleSleep fix ablated',
})
.not.toBeNull();
const { passed, failed } = parseSummary(findSummary(testLogger.consoleLogs)!);
expect(passed).toBe(1);
expect(failed).toBe(0);
expect(crashLines(testLogger), 'no crash signatures in console').toHaveLength(0);
expect(realErrors(testLogger), 'no page errors').toHaveLength(0);
});
});