task core: record tick's poison marker; keep the bracket broken on cancellation-delivery traps - #164
Merged
Merged
Conversation
…ncellation-delivery traps Two conformance fixes out of the definitions.py parity review. 1. Store.tick's poison path called the raw onInstancePoisoned hook instead of notifyInstancePoisoned, so an instance poisoned by a trap under tick (a background/sibling thread, outside any export's entered set — exec/boundary.ts's poison only marks entered-set members) was never recorded in the poisonedInstances marker. Every marker consumer then misfired for exactly that instance: Thread.resumeWith's quiet-retire of late settled tails missed (#156's backstop assert became reachable again pre-#161), #161's dispatchableTail deferred its tails forever (a silent leak, invisible to hasServiceableSettled — the observed pre-fix mode on current main), and withPoisonCause lost the #145 diagnostic suffix. One line: route through the seam like every other bracket-break site. 2. Task.requestCancellation wrapped the cancellation delivery in try/finally { leaveTo }, un-poisoning a half-unwound callee. The reference (definitions.py request_cancellation, lines 519-532) has no handler around resume(Cancelled.TRUE): a trap skips leave_to — instance poisoning — the discipline Store.tick preserves with its "deliberately NOT a finally" catch. Reachable via subtask.cancel delivering TASK_CANCELLED to a cancellable block point whose resumption traps (e.g. task.cancel with live borrows). Now mirrors tick's catch exactly: capability signals release the bracket, traps keep it broken, release the synthetic root (per-instance poisoning, plan v3 amendment 4), and notify the poison seam. Regression tests pin: the marker is recorded on tick's trap path and a late settled tail of the poisoned instance drains quietly (pre-fix: deferred forever); a trapping cancellation delivery leaves the callee locked, poisoned, and in cancel-delivered state (pre-fix: enterable); a capability signal during delivery still releases the gate and does not poison. Gates: just gates (full pass; conformance 1257/0, no expectation changes; sched-seeds clean). No published-surface change; lockstep version untouched.
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.
task core: record tick's poison marker; keep the bracket broken on cancellation-delivery traps
Two conformance fixes from the definitions.py parity review (five parallel independent review tracks against the pinned reference; both findings re-verified in source before dispatch).
1.
Store.tick's poison path bypassed the poison markerThe catch block called the raw
onInstancePoisonedhook instead ofnotifyInstancePoisoned, so an instance poisoned by a trap undertick— a background/sibling thread, outside any export's entered set (boundary.ts'spoisononly marks entered-set members) — was never recorded inpoisonedInstances. Every marker consumer misfired for exactly that instance:Thread.resumeWith's quiet-retire of late settled tails missed it — pre-scheduler: settled tails defer while their instance is not host-enterable #161 that resurrects the backstop assert;main, scheduler: settled tails defer while their instance is not host-enterable #161'sdispatchableTailsaw "non-enterable, non-poisoned" and deferred the instance's settled tails forever — a silent leak invisible tohasServiceableSettled. This is the observed pre-fix failure mode of the new regression test (verified against a stash);withPoisonCauselost the Go components trap (and poison their instance) when the Go runtime reads the clock inside cabi_realloc #145 diagnostic suffix on later entry refusals.Reference anchor: definitions.py
Store.tick(~:597) — the trap skippingleave_tois the poisoning this marker records. Fix is one line: route through the seam like every other bracket-break site (thread.ts, boundary.ts, fact_calls.ts, handles.ts).2.
Task.requestCancellationun-poisoned on a trapping deliveryCancellation delivery was wrapped in
try/finally { leaveTo(caller) }. The reference (Task.request_cancellation, definitions.py:519-532) has no handler aroundresume(Cancelled.TRUE): a trap skipsleave_to— the Component Model's instance poisoning, the same disciplineStore.tickpreserves with its "deliberately NOT afinally" catch. ThefinallyrestoredmayEnteron a half-unwound callee, leaving it enterable where the reference permanently locks it.Reachable:
subtask.canceldelivering TASK_CANCELLED to a cancellable block point whose resumed path traps (e.g.task.cancelwith live borrows — thenum_borrowstrap in the same file). Now mirrorstick's catch exactly: capability signals (NeedsJspi/PendingCapability) release the bracket (the runtime is incomplete, not the component faulted); traps keep it broken, release the synthetic root (poisoning stays per-instance, plan v3 amendment 4 — a no-op for a guest caller, whose entering set is the leaf alone), and notify the poison seam.statestayscancel-delivered, matching the reference's pre-delivery assignment.Tests
Three regression pins in task_test.ts, pre-fix failure modes confirmed against a stash:
expected true, got falseon the marker, and the tail deferred forever);mayEnterFrom(caller) === false), poisoned, and incancel-delivered(pre-fix: enterable);Gates
just gates— full pass. Conformance 1416 commands: 1257 passed, 0 failed, no expectation changes;sched-seedsclean. No published-surface change; lockstep version untouched per AGENTS.md.