docs 22: the one-root constraint that orders the B+C+D wiring
The scheduler fiber and libcontext's root would both adopt the main stack - two emscripten_fiber_t describing one stack, mutual corruption on first entry. Harmless in Phase A (KiCad never called drain), fatal at the flip. So dispatch must move onto a context FIRST, the libcontext root then adopts the running context rather than the main stack, and only then can jump_fcontext become a star transfer. Also records that the synchronous-Call question is answered and pinned. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EarUW9DS1c1sSW4ZNrkGQS
This commit is contained in:
parent
258cb37487
commit
ff4b2bdb3d
1 changed files with 32 additions and 0 deletions
|
|
@ -165,6 +165,38 @@ in it is **wrong** and must not come back — one dispatch context suffices once
|
|||
> **B + C + D land as ONE commit.** Each alone regresses. Build them on a sub-branch,
|
||||
> keep every guard as a tripwire, flip once, gate hard.
|
||||
|
||||
**Sub-branch `feature/async-star-flip` (2026-08-06): the core mechanism is built and
|
||||
proven, the wiring is not.** Landed there: `fiber_transfer(from, to, value)` —
|
||||
libcontext's symmetric swap expressed as a star transition (park the source, make the
|
||||
target runnable, the scheduler performs every entry) — plus `fiber_start` (the lane's
|
||||
entry point, since a transfer needs a running context to park) and `drain_all` (the
|
||||
top-level pump, because a star transition only makes work *runnable*).
|
||||
|
||||
The question that decided whether B is possible at all is answered YES and pinned by
|
||||
`star_transfer_call_is_synchronous`: **parking the caller and resuming it when the
|
||||
callee yields is indistinguishable, in the caller's own C++ frame, from a synchronous
|
||||
return.** So `TOOL_MANAGER`'s `Call(); if( !Running() )` contract survives the flip
|
||||
untouched — which is what makes a KiCad-minimal Phase B realistic.
|
||||
|
||||
**The constraint that fixes the order of the remaining wiring — ONE root, not two.**
|
||||
`ensure_scheduler_context()` adopts whatever stack first calls `drain()`, and
|
||||
libcontext's `ensure_main_context()` adopts whatever stack first calls
|
||||
`jump_fcontext`. In production both are the main stack, so the scheduler fiber and the
|
||||
libcontext root would be two `emscripten_fiber_t`s describing the SAME stack — mutual
|
||||
corruption the moment either is entered. In Phase A this was harmless (nothing in
|
||||
KiCad ever called `drain()`); at the flip it is fatal. Therefore:
|
||||
|
||||
1. **D first, inside the flip:** dispatch moves onto its own context, so the main
|
||||
stack is only ever the scheduler and nothing else runs there.
|
||||
2. libcontext's root then adopts **the running context** (the dispatch context), never
|
||||
the main stack.
|
||||
3. Only then may `jump_fcontext` become `fiber_transfer`, because every caller is now
|
||||
provably on a context and can park.
|
||||
4. C follows naturally: `wxWasmYieldUntil` yields the owning context, `resolveWait`
|
||||
marks it ready, and the doc-19 mainstack bounce comes out.
|
||||
|
||||
The tick becomes `fiber_start(dispatch, …)` + `drain_all()` from a fresh JS task.
|
||||
|
||||
### Phase E — bridges on contexts (1 wk)
|
||||
|
||||
Doc 21 §1's K1–K7 and W4/W5 through one `wasm_await_promise`-style helper. After this **no
|
||||
|
|
|
|||
Loading…
Reference in a new issue