Skip to content

driver: an export call is not done while any activation is hop-parked (#280) - #283

Merged
lannbot merged 1 commit into
mainfrom
fix-280-orphaned-hop
Sep 5, 2026
Merged

driver: an export call is not done while any activation is hop-parked (#280)#283
lannbot merged 1 commit into
mainfrom
fix-280-orphaned-hop

Conversation

@lannbot

@lannbot lannbot commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

The export driver's exit predicate (resolvedSeen && !midWasmCall())
scoped its mid-wasm-call test to the export's OWN task. When a background
task's async host import settled on a microtask while another export
call's driver was live, that driver's tick resumed the background
task's callback activation — a promising entry, hop-parked in
store.awaiting with no SuspensionPoint owner — and then declared itself
done. Nobody owned the hop: the settlement pump arms only on outstanding
real host calls, and the one that caused the resumption had already
settled. The activation sat unserviced until an unrelated later call
drove the store (or forever). A settle one macrotask later dodged it only
because no driver was live at settle time and the pump owned it.

A hop is something the driver itself put in flight and that lands within
a microtask on the engine's own schedule; the driver that caused it must
see it land. The predicate now also requires no hop-parked thread of ANY
task store-wide, via entryHopThreads generalized to an optional
instance filter. midWasmCall stays for the export task's own genuinely
JSPI-suspended thread, which the hop test deliberately excludes.

Repro pinned in e2e_cancel_import_test.ts: the cancel-import guest's
detached task parked on a hand-held promise, resolved synchronously as a
second export call's driver starts; pre-fix the driver exits with
awaiting=1 hostCalls=0 (the issue's #25 EXIT-done trace verbatim).

Closes #280.

Gates run locally: check, test-runtime, conformance, sched-seeds, smoke-c0, smoke-tls, test-ct-runner, test-bundle.

Automerge armed. The await macrotask() workaround in polyengine-dioxus host/src/eval.ts can be removed once this ships.

…#280)

The export driver's exit predicate (`resolvedSeen && !midWasmCall()`)
scoped its mid-wasm-call test to the export's OWN task. When a background
task's async host import settled on a microtask while another export
call's driver was live, that driver's `tick` resumed the background
task's callback activation — a promising entry, hop-parked in
`store.awaiting` with no SuspensionPoint owner — and then declared itself
done. Nobody owned the hop: the settlement pump arms only on outstanding
real host calls, and the one that caused the resumption had already
settled. The activation sat unserviced until an unrelated later call
drove the store (or forever). A settle one macrotask later dodged it only
because no driver was live at settle time and the pump owned it.

A hop is something the driver itself put in flight and that lands within
a microtask on the engine's own schedule; the driver that caused it must
see it land. The predicate now also requires no hop-parked thread of ANY
task store-wide, via `entryHopThreads` generalized to an optional
instance filter. `midWasmCall` stays for the export task's own genuinely
JSPI-suspended thread, which the hop test deliberately excludes.

Repro pinned in e2e_cancel_import_test.ts: the cancel-import guest's
detached task parked on a hand-held promise, resolved synchronously as a
second export call's driver starts; pre-fix the driver exits with
`awaiting=1 hostCalls=0` (the issue's `#25 EXIT-done` trace verbatim).

Closes #280.
@lannbot
lannbot enabled auto-merge September 5, 2026 04:35
@lannbot
lannbot merged commit 02438e3 into main Sep 5, 2026
3 checks passed
@lannbot
lannbot deleted the fix-280-orphaned-hop branch September 5, 2026 04:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async host import issued from an export's initial activation is lost if its promise settles in the next microtask checkpoint

2 participants