mailbox S1: shim delivery tick, embind audit, app-side WasmMailbox
Shim: mailbox FIFO + self-armed delivery tick calling wxWasmMailboxTick (plain export — never inside a pump's awaited ccall); injector sentinel fixed (the old marker also matched evtloop's EM_JS probe text). Doc 18: 79-export embind audit (14+3 production mutators to wrap, 20 pure-read allowlist, asymmetries). web/standalone WasmMailbox: FIFO defer-until- settled keyed on the proxy-safe kicadOpenFileBusy probe, 7 vitest green. Dual-variant wx battery green (28+39+7 both variants). Bump wxwidgets. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TfxKn5utcntBSnxz4ZnYKs
This commit is contained in:
parent
94ae4a8a41
commit
61b5f266fb
7 changed files with 419 additions and 15 deletions
|
|
@ -39,6 +39,15 @@ Also carried forward: **the root/scheduler context must never itself await JS**
|
|||
**`ProcessEvents` must be driven wasm-side**, not via `await ccall(...,{async:true})`
|
||||
(Emscripten #13302; 12 §prior-art). v0.1.28's fresh-task tick is the first brick of exactly that.
|
||||
|
||||
> **S1 lesson (2026-08-05, learned red-first):** the #13302 boundary constrains the MAILBOX
|
||||
> too — delivering messages from inside pump-driven `ProcessEvents` puts a fiber-swapping
|
||||
> handler inside the modal pump's awaited ccall and traps `unreachable`
|
||||
> (`coroutine-nested` / `fiber_create_run_destroy_inside_modal`). Deliveries must enter via
|
||||
> a dedicated plain export (`wxWasmMailboxTick`, self-armed from the shim, 17 ms retry while
|
||||
> the interlock is held) — the mailbox changes *when* a message runs, never the kind of
|
||||
> stack it runs on. This constraint binds every future lane (DOM input, embind) until S3
|
||||
> removes the awaited-ccall pumps entirely.
|
||||
|
||||
## 2. Cutover mechanics
|
||||
|
||||
- **Build flag `WX_SCHEDULER=1`** producing parallel glue variants, same pattern as
|
||||
|
|
@ -139,6 +148,18 @@ rollback = the `WX_SCHEDULER=0` build + a per-step tag.
|
|||
> `tests/kicad/mailbox-ordering.spec.ts` (N2, `test.fixme` red — add-then-move ordering
|
||||
> probe; un-fixme at S1). **Still open in S0:** the CI workflow matrix for both EH models.
|
||||
- **S1 · Mailbox front-end (≈1 wk).** One queue, drained by `wxWasmTopLevelTick`. Route into it:
|
||||
> **Work log 2026-08-05 — timers routed, dual battery green.** JS FIFO in the shim;
|
||||
> `wx/wasm/private/mailbox.h` + drain in `evtloop.cpp`; `timer.cpp` enqueues on scheduler
|
||||
> builds (runtime-gated on the shim marker; legacy path untouched; 17 ms retry kept as
|
||||
> tripwire). Delivery enters via the dedicated plain export `wxWasmMailboxTick` (see the
|
||||
> S1 lesson above — the first pump-embedded delivery design trapped in `coroutine-nested`
|
||||
> and was fixed red→green). Battery on BOTH variants: wx-chromium 28/28 (timer, dialog,
|
||||
> dialogs, contextmenu, popup) + coroutine-firefox 39/39 (incl. raytrace multicore) +
|
||||
> asyncify-firefox 7/7. App-side `WasmMailbox` wrapper (web/standalone/src/wasm/mailbox.ts,
|
||||
> 7 vitest green) keys on the proxy-safe `kicadOpenFileBusy` probe — PROXY_TO_PTHREAD makes
|
||||
> the window blind to the worker-side interlock; precision moves worker-side at S4.
|
||||
> **Still open in S1:** DOM-input lane, wiring the wrapper into the 14 production mutators
|
||||
> (needs the kicad wasm build + e2e), N2 un-fixme, beacon-silence assertions in fuzz runs.
|
||||
timer `Notify` (replacing the direct callback body; the 17 ms retry stays as tripwire), DOM
|
||||
input (formalizing today's `wxPostEvent`/`CallAfter` deferrals), and a JS-side wrapper for
|
||||
**mutating** embind entries (enqueue + returned promise). Deliverable alongside: the **sync
|
||||
|
|
@ -193,6 +214,10 @@ first step with user-visible semantic changes.
|
|||
- **Sync embind policy** (S1 audit): pure reads that never dispatch/park stay synchronous;
|
||||
everything else becomes a promise-returning message. The audit's deliverable is the exact
|
||||
allowlist, checked by a lint or a dev-build assert.
|
||||
> **DONE 2026-08-05 → [`18-embind-audit.md`](18-embind-audit.md):** 79 exports — 47 mutators
|
||||
> (14 production + 3 saves to wrap; 33 test-only stay direct), 20 pure-reads (the allowlist,
|
||||
> with the model-walk caveat), 9 park-capable. Asymmetries to fix listed there (ungated
|
||||
> pure-reads, pl_editor bare-stack applies, ungated `kicadLibsReload`).
|
||||
- **`kicadOpenFileBusy` compatibility**: `open-flow.ts` feature-detects it and legacy wasm
|
||||
builds rely on the fallback — keep the export, backed by scheduler state, until the web app
|
||||
drops support for pre-scheduler wasm.
|
||||
|
|
|
|||
81
docs/features/async/18-embind-audit.md
Normal file
81
docs/features/async/18-embind-audit.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# 18 — Embind export audit (doc 17 S1 deliverable)
|
||||
|
||||
> **Status: AUDIT (2026-08-05).** The sync-embind classification doc 17 §4 S1 calls for:
|
||||
> every JS-callable export in `wasm/bindings/`, classified for the mailbox migration.
|
||||
> Method: all `EMSCRIPTEN_BINDINGS` blocks enumerated; each body followed one level deep.
|
||||
> Classes: **PURE-READ** (stays synchronous), **MUTATOR** (becomes a queued message),
|
||||
> **PARKER** (can asyncify-park itself), **TEST-LEVER** (`kicadTest*`, e2e only).
|
||||
|
||||
## Headline numbers
|
||||
|
||||
**79 distinct JS names** (171 `function()` registrations across 6 bindings blocks —
|
||||
names duplicate per-bundle behind `#ifndef KICAD_MERGED_EMBIND`; pcb_calculator's block
|
||||
is empty). No `EMSCRIPTEN_KEEPALIVE` JS entries exist in the tree.
|
||||
|
||||
| class | count | migration consequence |
|
||||
|---|---|---|
|
||||
| MUTATOR | 47 (33 test-only, 14 production) | queued mailbox message (S1 wrapper / S4) |
|
||||
| PURE-READ | 20 | stays sync — the allowlist |
|
||||
| TEST-LEVER | 9 (6 of them park-capable by design) | keep direct — they *stage* collisions |
|
||||
| PARKER | 3 production (`kicadOpenFile`, `kicadOpenFiles`, `kicadLibsReload`) | fiber contexts at S4 |
|
||||
|
||||
**The 14+3 production mutators/IO:** `kicadSetChrome`, `kicadSetReadOnly`,
|
||||
`kicadCollabApply`, `kicadCollabApplyItems`, `kicadCollabSnapshot`,
|
||||
`kicadCollabSnapshotItems`, `kicadCollabPresenceStart`, `kicadCollabSetRemote`,
|
||||
`kicadCollabSetPins`, `kicadCollabSetStyle`, `kicadCollabSetViewport`,
|
||||
`kicadCollabFitViewport`, `kicadCollabReleaseSelection`, `kicadSetColorTheme`
|
||||
(park-suspect: settings Save + full chrome rebuild), plus light `kicadSetDarkChrome`
|
||||
and the three bare-stack I/O entries `kicadSaveBoard` / `kicadSaveSchematic` /
|
||||
`kicadSaveDrawingSheet`.
|
||||
|
||||
**The PURE-READ allowlist (stays sync):** `kicadOpenFileBusy`, `kicadCollabFiberBusy`,
|
||||
`kicadCollabGetPos`, `kicadCollabGetViewport`, `kicadCollabGetSelection`,
|
||||
`kicadCollabGetSelectionFull`, `kicadCollabTestGetCrossMapped`, `kicadCollabTestGetLocked`,
|
||||
`kicadCollabTestListItems`, `kicadCollabTestDemoSet`, `kicadCollabTestItemBlob`,
|
||||
`kicadCollabTestUndoDepth`, `kicadTestTimerParkState`, `kicadTestFiberParkState`,
|
||||
`kicadLibsSymbolUsage`, `Board_GetFootprints`, `Board_GetFileName`, `Footprint_GetPads`,
|
||||
`Footprint_GetReference`, `Footprint_GetValue`, `Pad_GetNumber`, `Pad_GetPinFunction`.
|
||||
Caveat below: "sync" is safe for the asyncify machinery, but reads that walk the model
|
||||
still need the open to have settled (N6 territory).
|
||||
|
||||
## Guard coverage today
|
||||
|
||||
| guard | coverage |
|
||||
|---|---|
|
||||
| `pcbjam_open::BusyGuard` (sets the gate) | the 6 open entries |
|
||||
| `pcbjam_open::busy()` early-return | ONLY 8 names: Collab{Apply,ApplyItems,Snapshot,SnapshotItems} × pcbnew/eeschema/pl_editor |
|
||||
| `runOnFiber` FIFO (collab_common.h) | ~35 mutators |
|
||||
| bare `CallAfter`, no fiber | selection/presence sub-paths, eeschema's 2 move test hooks |
|
||||
| entirely unguarded, bare embind stack | `kicadSetChrome`, `kicadSetReadOnly`, the 3 `kicadSave*`, pl_editor applies (busy-gated but not fibered, inline `HardRedraw`), `kicadCollabTestAddText`, `kicadLibsReload` (fibered, no busy check), and every model-walking PURE-READ |
|
||||
|
||||
## Asymmetries the migration must fix (or knowingly accept)
|
||||
|
||||
1. **Model-walking pure-reads have no `busy()` gate** (`kicadCollabGetPos`,
|
||||
`kicadCollabGetSelection*`, `kicadCollabTestListItems`, `Board_*`/`Footprint_*`/`Pad_*`,
|
||||
`kicadLibsSymbolUsage`, `kicadCollabTestItemBlob`) — they can walk a half-built model
|
||||
during a parked `OpenProjectFiles`. Same exposure class the gate was built for; the
|
||||
mailbox does NOT fix reads (they stay sync) — needs either a busy() early-return with a
|
||||
benign empty result, or documented "caller must await settle" (open-flow already does).
|
||||
2. **pl_editor's `kicadCollabApply`/`ApplyItems` are busy-gated but NOT fibered** — they
|
||||
mutate `DS_DATA_MODEL` + `HardRedraw()` on the bare embind stack (pl_editor_embind.cpp:313,
|
||||
:515), unlike the pcbnew/eeschema twins. `kicadCollabTestAddText` (PL:597) likewise.
|
||||
3. **`kicadLibsReload` is the only production PARKER with no `busy()` gate**
|
||||
(pcbjam_libs_reload.h — `LoadLibraryEntry` asyncify-suspends per its own header).
|
||||
4. **eeschema's `schCollabTestMoveFirst`/`MoveSchItem` use bare `CallAfter`** where the
|
||||
pcbnew twins use `runOnFiber`.
|
||||
5. **Split-context construction**: `kicadCollabTestDuplicate*`/`AddSymbol` clone/construct
|
||||
items off-fiber then commit on-fiber.
|
||||
6. `kicadSave*` park-safety rests on MEMFS writes staying synchronous — UNCERTAIN, verify
|
||||
before S4 puts saves on fibers.
|
||||
7. Registration gaps: `kicadSetChrome` merged-image only; the timer/fiber park test levers
|
||||
exist in kicad_editor + pcbnew only.
|
||||
|
||||
## Consequences for the S1 wrapper (doc 17 S1.5)
|
||||
|
||||
Wrap-and-enqueue applies to the **14 production mutators + 3 saves**; test-only mutators
|
||||
keep direct entry (the harness *wants* to stage collisions). The busy()-gated four
|
||||
(apply/snapshot pairs) are the first wrap targets — their drop→deliver flip is N2's
|
||||
subject. `kicadSetDarkChrome` is documented main-thread-safe and can stay direct.
|
||||
|
||||
Full per-export table (file:line, evidence, guard) lives in the audit transcript; the
|
||||
classifications above are the binding contract for S1/S4 work.
|
||||
Loading…
Reference in a new issue