lift atomicity: gate host entry on jspi hop quiescence; retire poisoned late-settles - #82
Merged
Conversation
…ed late-settles In jspi mode a lifted export's entry is promising-wrapped, so even a guest turn that completes synchronously settles a microtask later (pin (j)) — a HOP between the guest's core return and the host-side result lift. The invoke wrapper releases the reentrance bracket when the first segment parks (including hop-parks), so a second host call could enter and run a full guest turn against memory the pending lift was about to read. definitions.py canon_lift (sync options) runs core + lift atomically inside one bracket; the hop window has no counterpart there. Found by the wosh consumer within minutes of bumping past the parking kernel (whose marked wasi imports auto-detect plain sync components into jspi mode): the mosh engine's tick -> list<list<u8>> lift read a concurrent feed-keys turn's reallocated memory — Trap: list too long — poisoning the instance; every later settle of the instance's parked threads then died on resumeWith's enterability assert, burying the real trap under an assert cascade. Fix, two halves: - exec/boundary.ts: THE HOP-QUIESCENCE GATE — a host call into a jspi-mode instance defers (awaitHopQuiescence) while the instance has a hop-parked activation: an awaiting thread with no owning SuspensionPoint, the same (b)/(c) discriminator hasRunnableWork uses. Genuine JSPI suspensions keep the documented interleaving divergence (host-import re-entry relies on it); plain mode keeps its synchronous fast path untouched. Progress is guaranteed — hops settle on the engine's schedule independent of other activations. - task/thread.ts + scheduler.ts: poisoned-instance late settles are RETIRED quietly instead of assert-cascading (poisonedInstances WeakSet beside the #66 seam; boundary's invoke-path poison now routes through notifyInstancePoisoned so the marker is recorded). The abandoned call's driver reports via its deadlock trap naming the export; the ORIGINAL trap stays the loud one. Regression pin: runtime/tests/jspi/hop_atomicity_test.ts over fixtures/hop-atomicity.wat — tick() -> list<list<u8>> plus clobber() that overwrites the return area, flipped into jspi mode by a suspending()-marked import the guest never calls (the exact wosh mechanism; asserts !planNeedsSuspension so the provenance stays honest). Deterministic: no timing. Fails pre-fix with the wild symptom (list too long), passes post-fix incl. a self-heal round 2. Gates: test-runtime 382/0, sched-seeds (1, 4242), conformance 1254/0, test-wasi-shims 52/0, smoke-c0 4/4, websocket-conformance 55/55. Consumer verification: the wosh browser-pump reproducer (bundled, real mosh traffic) green 3/3 (failed first-try pre-fix).
This was referenced Aug 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consumer-found (wosh, minutes after bumping past #71): in jspi mode the promising-entry HOP — core returned, result lift pending, bracket already released — admitted a second host call whose guest turn reused the un-lifted return area.
Trap: list too longlifting the mosh engine'stick(list<list<u8>>), then an assert cascade off the poisoned instance's late settles.canon_lift(sync options) runs core+lift atomically; the hop window has no reference counterpart.hasRunnableWork's own discriminator). Genuine JSPI suspensions keep the documented interleaving divergence; plain mode's synchronous fast path is untouched.notifyInstancePoisonedso the marker is recorded at every bracket-break site.hop_atomicity_test.ts+fixtures/hop-atomicity.wat(tick/clobber; jspi via asuspending()-marked import the guest never calls — the exact wosh mechanism). Fails pre-fix with the wild symptom; no timing dependence.Gates: test-runtime 382/0, sched-seeds both, conformance 1254/0, wasi-shims 52/0, smoke-c0 4/4, websocket 55/55. Consumer: the wosh bundled real-traffic reproducer green 3/3 (failed first-try pre-fix).