pcbjam/docs/features/async/18-embind-audit.md
Gergő Törcsvári 61b5f266fb
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
2026-08-10 10:14:12 +02:00

5.1 KiB
Raw Blame History

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.