lift: branch on the driver's exit verdict, not a re-derived done() (0.6.6 regression) - #312
Merged
Merged
Conversation
An async-typed lifted export's driver exits on one of two verdicts: `done()` held (task.return seen, no wasm call or hop in flight) or idle (#292 / #310). Since #310 the lift RE-EVALUATED `driveDone()` in its `.then` continuation to decide between `finishHostEntry` and `backgroundCompletion`. That predicate is time-varying: one microtask after an EXIT-done, another driver of the same store (the settlement pump servicing a settled host call) can resume a background activation that transiently hop-parks, so `midWasmCall()`/`hopParked()` read true and the lift took `backgroundCompletion()` — which resolves on `Task.onFinished`, i.e. never for a callback-ABI task whose spawned futures are immortal. Regression from 0.6.5, whose continuation was `pending.then(finishHostEntry)` unconditionally. Observed in polyvisor: `lifecycle.boot` (spawns the engine driver, event pump and accept loop, then task.returns) never resolved on a reload once a store sync task added enough host-call traffic for the pump to win the race. Trace: `EXIT-done ... done=true` followed by `-> backgroundCompletion threads=1 resolvedSeen=true`. Fix: `drive`/`driveLoop`/`driveAsync` return their exit verdict (`DriveExit = "done" | "idle"`) and the lift branches on that. The verdict is a fact about the loop that ran; re-deriving it later asks a different question. `driveStoreAsync` discards the verdict (the pumps have no completion to route). Test: `runtime/tests/lift_done_verdict_test.ts` — a callback-ABI lift with an immortal second thread task.returns and exits done; a foreign thread hop-parks in the microtask window before the continuation. Red on main ("expected resolved, got pending"), green with the fix.
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.
An async-typed lifted export's driver exits on one of two verdicts:
done()held (task.return seen, no wasm call or hop in flight) oridle (#292 / #310). Since #310 the lift RE-EVALUATED
driveDone()inits
.thencontinuation to decide betweenfinishHostEntryandbackgroundCompletion. That predicate is time-varying: one microtaskafter an EXIT-done, another driver of the same store (the settlement
pump servicing a settled host call) can resume a background activation
that transiently hop-parks, so
midWasmCall()/hopParked()read trueand the lift took
backgroundCompletion()— which resolves onTask.onFinished, i.e. never for a callback-ABI task whose spawnedfutures are immortal. Regression from 0.6.5, whose continuation was
pending.then(finishHostEntry)unconditionally.Observed in polyvisor:
lifecycle.boot(spawns the engine driver,event pump and accept loop, then task.returns) never resolved on a
reload once a store sync task added enough host-call traffic for the
pump to win the race. Trace:
EXIT-done ... done=truefollowed by-> backgroundCompletion threads=1 resolvedSeen=true.Fix:
drive/driveLoop/driveAsyncreturn their exit verdict(
DriveExit = "done" | "idle") and the lift branches on that. Theverdict is a fact about the loop that ran; re-deriving it later asks a
different question.
driveStoreAsyncdiscards the verdict (the pumpshave no completion to route).
Test:
runtime/tests/lift_done_verdict_test.ts— a callback-ABI liftwith an immortal second thread task.returns and exits done; a foreign
thread hop-parks in the microtask window before the continuation.
Red on main ("expected resolved, got pending"), green with the fix.
Consumer evidence: polymorph-components/polyvisor,
deno task e2eon 0.6.6 failsdrive-connect/drive-round-trip(boot never resolves on reload); forcingtrapOnIdle: trueor latching the verdict makes all 16 scenarios pass. Traces:POLYENGINE_DRIVE_TRACE+ a log at the lift's exit sites.Not in this PR (to file separately):
backgroundCompletionresolves onTask.onFinished(last thread unregisters) rather than on the task's resolve callback, so a task with long-lived spawned futures that idles beforetask.returnnever answers the host even after it returns.Automerge armed.