src: Deliver a pending ^C to scoped tasks without the listener
The C side of ^C delivery marks the episode source directly, but a
task running under a scoped child source (e.g. inside @sync) polls its
own source at cancellation points - carrying the episode's
cancellation down to it required the julia-side sigint listener's tree
walk. In a single-threaded process (julia's default) whose only thread
runs a compute-bound task, the listener never gets scheduled, so the
first ^C never landed and only the escalation ladder could get
through.
The per-thread cancellation-delivery paths - which every ^C already
triggers on every thread - now carry the pending episode into the
interrupted task's bound source themselves: if the source is governed
by the episode source (parent-chain membership), the episode's state
byte is CAS-maxed into it, a single async-signal-safe write, and the
task's next cancellation point observes it with no julia-side
scheduling at all. The listener's eventual walk redoes the remaining
bookkeeping level-triggered. Three gates had to open for this to work:
the dispatch-pending flag is set before the per-thread sends (the
handlers race it otherwise), and the senders no longer require a
published interruptible-region context when a ^C dispatch is pending
and the task carries a binding - a purely polling victim between
cancellation points never has one.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KLwCBAR64r3cTdfaC5krU