WIP: cancellation: Hook up ^C and put everything together - #62648
Closed
Keno wants to merge 10 commits into
Closed
Conversation
Member
Author
|
Some of this is separable - I'll split this up some more. |
Keno
force-pushed
the
kf/cancellation-sigint
branch
5 times, most recently
from
August 6, 2026 08:38
ecb661c to
5b77e7a
Compare
Replace the old ^C delivery - throwing an InterruptException into whatever the root task was doing at a GC safepoint - with delivery through the cancellation system: SIGINT marks the ^C episode source cancelled directly from the signal path (async-signal-safely, so a single-threaded process stuck in compute still observes it) and notifies a Julia-side listener task that performs the remaining delivery work (waking parked waiters). The per-thread cancellation sends also propagate a pending ^C episode into the interrupted task's own bound descendant source, so scoped computations observe the press even when the listener is starved, and idle threads take over a stalled event loop so the notification can always be dispatched. The episode source itself is installed by Base around each foreground evaluation (see the companion commit); `exit_on_sigint` mode is unchanged. This pull request was written with the assistance of generative AI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add the Julia half of the new ^C delivery: a sigint listener task per nonempty threadpool claims each notification and completes the delivery (cancelling a fresh episode source, or re-running the level-triggered delivery pass for one the signal path already marked - `Base.redeliver!` - so parked waiters get their wake), and interactive drivers install a fresh ^C episode source per foreground evaluation - so a press cancels exactly that evaluation's scope - closing it when the work item completes. Episode sources are linked under the session source, keeping the work an evaluation leaves behind sweepable by `Base.cancel_session_work!`. `@sync`, `Threads.@threads` and `Experimental.@sync` blocks now scope a cancellation source over their children, with teardown that keeps awaiting internal tasks across severity escalations and re-arms its waits at each rung. This pull request was written with the assistance of generative AI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The sigint listener task started from `Base.__init__` is not statically reachable in trimmed binaries; pin JuliaC to a staging branch whose trim overlay stubs it out, until the stub lands upstream. This pull request was written with the assistance of generative AI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> (cherry picked from commit 3595391802040e7a344d00d256c3e1567556b7fb)
Subprocess and pty scenarios for the new SIGINT semantics: catching and re-arming in scripts, propagation through `@sync`/`Threads.@threads`/ `Experimental.@sync` scopes (including teardown that keeps awaiting internal tasks across explicit severity escalations), the episode re-arm/close cycle in the REPL, and the historical ^C crash classes (issues JuliaLang#4037-adjacent delivery, JuliaLang#25790, JuliaLang#29369/JuliaLang#45055, JuliaLang#42072, JuliaLang#43451, JuliaLang#47839, JuliaLang#50045, JuliaLang#56462, JuliaLang#56545, JuliaLang#58105, JuliaLang#58689, JuliaLang#49541's watcher shape). Existing tests that asserted the legacy force-thrown InterruptException contract are updated to the cancellation-request delivery. This pull request was written with the assistance of generative AI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Give `CANCEL_REQUEST_ABANDON_ALL` its documented delivery semantics: tasks that do not acknowledge the request are frozen, never to run again, instead of being interrupted. A running victim is ripped from its thread through the task-abandonment handshake; a parked or queued one is marked frozen where it stands (its stale registrations discard on later notifies); a task cancelled before it first ran completes into a new `cancelled` state that a late `schedule` transitions to failed instead of erroring. Spawning into a frozen scope is refused at task start - the freeze contract is that nothing under the scope makes further progress, and the spawned body's first cancellation point may lie arbitrarily far in. Sources additionally track which severities some task has *observed* (a `delivered` bitmask maintained at cancellation points and walk deliveries, async-signal-safe for C-side reads): the distinction between a request in flight and one whose target never saw it is what the ^C escalation ladder's rung selection is built on. This pull request was written with the assistance of generative AI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Build the ^C escalation ladder on top of the cancellation-based SIGINT delivery: every press arms a one-second rescue timer; if the process does not acknowledge the cancellation in time, the timer prints a rung-appropriate offer, and the next press climbs one rung (SAFE -> ABANDON_EXTERNAL -> ABANDON_ALL), with a C-side direct abandonment (polled, never waited for on the signal listener) as the fallback when no thread is available to run the julia-side listener at all. ^T/SIGINFO (or SIGUSR1) prints thread backtraces during an episode. This pull request was written with the assistance of generative AI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extend the sigint listener with the escalation state machine: retry undelivered requests, escalate one severity rung after the rescue timer's offer, freeze a foreground task without a published token binding at the final rung, and clean up a forcibly abandoned target - rescuing the REPL backend (or exiting the session when nothing can resume). The rescue task gives a thread whose current task was abandoned something runnable to switch to, and process exit disarms the rescue timer. The listener pass touches REPL code only after a target was actually frozen: invoking (and possibly compiling) it earlier can deadlock the compile's GC against a cancelled victim that is still spinning without safepoints - precisely the victim the remaining rungs must cut through. This pull request was written with the assistance of generative AI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each REPL work item runs under a fresh ^C episode token, closed when control returns to the prompt, so a ^C cancels exactly the current evaluation and an idle press is a no-op; result reporting is shielded so a cancelled epoch cannot lose the response. When the escalation ladder had to forcibly abandon the backend task, inform the frontend and restart the backend so the session survives. The rescue path is precompiled into the pkgimage: the sigint listener invokes it during a ^C episode, where a first-time compile could deadlock against an unresponsive victim. This pull request was written with the assistance of generative AI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Point Distributed at a staging branch where a worker-side `interrupt` cancels the remotely-submitted work's cancellation scope instead of throwing an InterruptException into an arbitrary task; retarget to JuliaLang/Distributed.jl once merged upstream. This pull request was written with the assistance of generative AI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ladder scenarios on top of the core ^C delivery tests: the rescue timer's escalation offers and rung climbing (subprocess and pty, at several thread counts), direct abandonment of unresponsive compute, the catch-all-swallowing loop that cannot hide from ^C, REPL backend rescue after an abandonment, and the threaded cancellation exec suite. This pull request was written with the assistance of generative AI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keno
force-pushed
the
kf/cancellation-sigint
branch
from
August 6, 2026 20:05
5b77e7a to
bd3b277
Compare
Member
Author
|
Closing in favor of #62663, which is the last piece. |
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.
Now that everything else is merged, this is the final extract from #60281 to actually turn everything on. As usual, WIP while I clean up after the AI.