The CM pin advance (polyengine#173) pulls three upstream cancellation-delivery changes that polyengine's task core predates. They form one coherent adoption track (xfail class cm707-cancel until this issue closes):
- CM#705's
canon_subtask_cancel rewrite (part of the may_enter removal commit, but additive): trap_if(subtask.in_waitable_set() and not async_) strengthens to an unconditional trap; the sync-wait path becomes subtask.has_sync_waiter = True + thread.wait_until(subtask.resolved) (replacing wait_for_pending_event). Our implementation of the pinned conditional form is runtime/src/intrinsics/async_builtins.ts (st.inWaitableSet() && !async_); hasSyncWaiter already exists on waitables for the stream paths.
- CM#707 — deliver cancellation as soon as possible: pending-cancel bookkeeping must (a) NOT deliver at non-cancellable suspension points (
thread.yield nc returns 0, request stays pending), (b) deliver at the next cancellable suspension, and (c) deliver when the callee's exclusive lock frees, even if the parked task's waitable set never fires.
6c67aa1 — deliver_pending_cancellation ordering fix (moved before stop_waiting_internal): verify our delivery site ordering against the merged definitions.py.
Corpus rows pinning the merged behavior:
test/async/cancel-and-exclusive-lock.wast (new, CM#707): subtask.cancel async on a callee whose lock is held returns BLOCKED and stores the request pending; delivery happens on lock release (TASK_CANCELLED → task.cancel → CANCELLED_BEFORE_RETURNED). A pre-#707 runtime hangs.
test/async/cancellable.wast (extended, CM#707): Test 5 pending-survives-nc-yield — single final assert covers all five tests, so the file fails wholesale until the nc-yield distinction exists.
test/async/trap-if-block-and-sync.wast (rewritten): the trap-if-sync-cancel case now requires real attempt-delivery-then-trap-on-block behavior; the four {stream,future}.cancel-{read,write} sync-trap directives are deleted upstream (guest-guest cancels never block under #707).
test/async/reentrance.wast case 9 also exercises immediate delivery with the callee on stack, but is xfailed under fact-reentrance-47 regardless (see the wasmtime-bump register issue).
Adjacent: #212 (trapping cancellation delivery poisons via the settled-tail path) — same territory; re-derive its parity question against the merged definitions.py while in there. The may_enter gate in Task.requestCancellation is removed by the #173 gate-removal PR, not this track.
Authority: design/mvp/canonical-abi/definitions.py at the advanced pin (tie-breaker). Task core is single-owner territory (AGENTS.md): coder-hard at most, close orchestrator review.
The CM pin advance (polyengine#173) pulls three upstream cancellation-delivery changes that polyengine's task core predates. They form one coherent adoption track (xfail class
cm707-canceluntil this issue closes):canon_subtask_cancelrewrite (part of the may_enter removal commit, but additive):trap_if(subtask.in_waitable_set() and not async_)strengthens to an unconditional trap; the sync-wait path becomessubtask.has_sync_waiter = True+thread.wait_until(subtask.resolved)(replacingwait_for_pending_event). Our implementation of the pinned conditional form isruntime/src/intrinsics/async_builtins.ts(st.inWaitableSet() && !async_);hasSyncWaiteralready exists on waitables for the stream paths.thread.yieldnc returns 0, request stays pending), (b) deliver at the next cancellable suspension, and (c) deliver when the callee's exclusive lock frees, even if the parked task's waitable set never fires.6c67aa1— deliver_pending_cancellation ordering fix (moved beforestop_waiting_internal): verify our delivery site ordering against the merged definitions.py.Corpus rows pinning the merged behavior:
test/async/cancel-and-exclusive-lock.wast(new, CM#707):subtask.cancel asyncon a callee whose lock is held returns BLOCKED and stores the request pending; delivery happens on lock release (TASK_CANCELLED →task.cancel→ CANCELLED_BEFORE_RETURNED). A pre-#707 runtime hangs.test/async/cancellable.wast(extended, CM#707): Test 5pending-survives-nc-yield— single final assert covers all five tests, so the file fails wholesale until the nc-yield distinction exists.test/async/trap-if-block-and-sync.wast(rewritten): thetrap-if-sync-cancelcase now requires real attempt-delivery-then-trap-on-block behavior; the four{stream,future}.cancel-{read,write}sync-trap directives are deleted upstream (guest-guest cancels never block under #707).test/async/reentrance.wastcase 9 also exercises immediate delivery with the callee on stack, but is xfailed underfact-reentrance-47regardless (see the wasmtime-bump register issue).Adjacent: #212 (trapping cancellation delivery poisons via the settled-tail path) — same territory; re-derive its parity question against the merged definitions.py while in there. The may_enter gate in
Task.requestCancellationis removed by the #173 gate-removal PR, not this track.Authority:
design/mvp/canonical-abi/definitions.pyat the advanced pin (tie-breaker). Task core is single-owner territory (AGENTS.md): coder-hard at most, close orchestrator review.