design-b D3: docs — doc 19 FIXED, and the plan re-scoped around that
Doc 19: status DIAGNOSED+PINNED RED -> FIXED, with the mechanism and the explicit note that waits still park in place; the cure was to stop parking on a COROUTINE stack, not to stop parking. Doc 20: D3 work log (layering table, gate numbers) and a status that no longer claims a phase queue. The bug that motivated the whole core rewrite is closed WITHOUT contexts, so D2/D4/D5/D6 have lost their forcing function and must be re-justified rather than continued by default — including the honest option of deleting D1's unused context layer. Trap recorded: detection must capture the main stack's bounds at top-level DoRun, not query them live — finishContextSwitch resets the limits to the incoming fiber's, so a live query reports "main stack" from everywhere and detects nothing. Gate: kicad 139 passed / 1 failed (pre-existing occ-probe glb); wx + asyncify + coroutine 394 passed / 1 failed (pre-existing environment-sensitive modal:125). coroutine-nested — the battery D2 regressed — is green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TEHGiiXMShNXbBr7gSJ7iz
This commit is contained in:
parent
48da418f34
commit
cc7c1d1f16
2 changed files with 63 additions and 7 deletions
|
|
@ -1,6 +1,20 @@
|
|||
# 19 — Symbol Properties dialog hangs: a stranded tool fiber (live investigation)
|
||||
|
||||
> **Status: DIAGNOSED + PINNED RED, NOT FIXED (2026-08-05).** Reproduced end-to-end in a
|
||||
> **Status: FIXED (2026-08-06).** The nested (quasi-modal) event loop no longer parks on
|
||||
> the tool coroutine's stack: `wxGUIEventLoop::DoRun` detects that its park would land on a
|
||||
> non-main stack and bounces it onto the main stack via the host runner
|
||||
> (`wx/wasm/private/mainstack.h` + `wasm/bindings/main_stack_runner.h` →
|
||||
> `TOOL_MANAGER::RunOnMainStackIfActiveTool`). The coroutine is then suspended the
|
||||
> legitimate way — a fiber swap the layer records — so §4's quarantine never applies and
|
||||
> its resume is never refused. `tests/kicad/quasimodal-strand.spec.ts` is a plain green
|
||||
> regression pin: 3/3 `closed=true dialogs=0 refused-resumes=0`.
|
||||
>
|
||||
> Note what this does NOT do: waits still park in place (§6 direction 3 — handler contexts —
|
||||
> is untaken). The cure was to stop parking on a *coroutine* stack, not to stop parking.
|
||||
>
|
||||
> Original diagnosis follows.
|
||||
>
|
||||
> ~~**Status: DIAGNOSED + PINNED RED, NOT FIXED (2026-08-05).**~~ Reproduced end-to-end in a
|
||||
> real browser against the dev platform on the scheduler build, and since D0 also
|
||||
> **deterministically in e2e**: `tests/kicad/quasimodal-strand.spec.ts` (6/6 identical —
|
||||
> `closed=false dialogs=1 refused-resumes=1`). The fix lands at doc 20 **D3**, where that
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
# 20 — Design B core: parkable activities as scheduler contexts
|
||||
|
||||
> **Status: IN PROGRESS — D-1, D0, D1 DONE; D2 attempted and resequenced; D3 started and
|
||||
> found to be larger than written (work log §10). Remaining order: **[tool contexts + D3
|
||||
> waits + D2 dispatch] as ONE atomic change** → D4 → D5? → D6.** The remaining core of
|
||||
> **Status: THE MOTIVATING BUG IS FIXED (2026-08-06). D-1, D0, D1 done; D2 attempted and
|
||||
> reverted; D3 achieved its goal — the doc-19 hang — WITHOUT contexts, by keeping
|
||||
> quasi-modal parks off coroutine stacks (work log §10). The remaining phases (D2, D4, D5,
|
||||
> D6) no longer have a bug forcing them and need re-justifying before any is resumed.** The remaining core of
|
||||
> Design B ([`06`](06-design-b-fiber-first-runtime.md) B1+B2), scoped against what S0–S6
|
||||
> actually built ([`17`](17-mailbox-scheduler-plan.md)) and motivated by the
|
||||
> stranded-fiber hang ([`19`](19-quasimodal-fiber-strand.md)). Supersedes doc 12's
|
||||
|
|
@ -357,6 +358,47 @@ Landed (wx `fc762bed`, pcbjam `0d40938`):
|
|||
alone regresses, as D2's attempt already showed. Doc 12's "partial migration is worse than
|
||||
none" is now measured twice, from two directions.
|
||||
|
||||
Next: the atomic D3+D2 landing, starting with tool coroutines as contexts — i.e. teaching
|
||||
the scheduler about `COROUTINE`/libcontext rather than adding a second fiber system beside
|
||||
it.
|
||||
### D3 — the doc-19 hang is FIXED, without contexts (2026-08-06) ✅
|
||||
|
||||
Starting the "tool coroutines as contexts" work turned up a much smaller cure, and it
|
||||
holds: **the quasi-modal's nested loop simply must not park on a coroutine's stack.**
|
||||
`wxGUIEventLoop::DoRun` now detects that its nested park would land on a non-main stack and
|
||||
bounces it onto the main stack; the coroutine is then suspended through a recorded fiber
|
||||
swap, so the stale-fiber guard has nothing to quarantine and no resume to refuse.
|
||||
|
||||
`tests/kicad/quasimodal-strand.spec.ts` flipped from `test.fail()` to a plain green pin —
|
||||
3/3 `closed=true dialogs=0 refused-resumes=0`, previously 3/3 the exact inverse. Full kicad
|
||||
suite: 139 passed, 1 failed (the pre-existing occ-probe `glb`).
|
||||
|
||||
Layering, chosen so this is not WASM ifdefs scattered through KiCad:
|
||||
|
||||
| layer | holds |
|
||||
|---|---|
|
||||
| wx wasm port (`3d37db3bf1`) | the policy + the hook (`wx/wasm/private/mainstack.h`). wx must not know what a coroutine is. |
|
||||
| `wasm/bindings/main_stack_runner.h` | the only place allowed to know both sides: frame → `TOOL_MANAGER` → `RunMainStack`. Header-only, self-installing, so no build-script change. |
|
||||
| KiCad (`2c777efede`) | **one ifdef-free method**, `RunOnMainStackIfActiveTool`, needed only because `TOOL_STATE` is opaque outside `TOOL_MANAGER`. `libcontext` and `dialog_shim` untouched (an earlier draft edited both; reverted). |
|
||||
|
||||
**Trap worth keeping:** detection captures the main stack's bounds at top-level `DoRun`
|
||||
instead of querying them live. `emscripten_fiber_swap`'s `finishContextSwitch` calls
|
||||
`emscripten_stack_set_limits` with the *incoming* fiber's bounds, so
|
||||
`emscripten_stack_get_base()/end()` always describe whatever stack is current — the live
|
||||
query reports "on the main stack" from everywhere. That version silently did nothing and
|
||||
looked like a regression until the glue was read.
|
||||
|
||||
**What this changes about the plan.** The bug that motivated the whole core rewrite is
|
||||
closed without contexts, so the remaining phases are no longer urgency-driven and should be
|
||||
re-justified on their own merits:
|
||||
|
||||
- The **doc-19 class** (a parked activity holding the interlock) is closed for quasi-modals.
|
||||
Other in-place parks (bridges, D4's list in doc 21) can still hold it — but none of them
|
||||
park on a coroutine stack, which was the fatal ingredient.
|
||||
- **D2 (dispatch contexts)** remains blocked exactly as measured, and now has no bug forcing
|
||||
it. Re-justify before retrying.
|
||||
- **D1's context layer** stays as built and tested; nothing production runs on it. Keep it
|
||||
only if D4/D5 still want it — otherwise it is a candidate for deletion rather than a
|
||||
half-migrated runtime.
|
||||
|
||||
Next: decide whether the remaining core is still worth its risk now that its motivating bug
|
||||
is gone. The honest options are (a) stop here and harden, (b) continue to D4 bridges for the
|
||||
remaining in-place parks, (c) delete the unused context layer. That is a judgement call for
|
||||
the human, not a mechanical next phase.
|
||||
|
|
|
|||
Loading…
Reference in a new issue