Skip to content

feat(cancellation): explore independent cancellation signals - #350

Draft
tisonkun wants to merge 1 commit into
mainfrom
codex/cancellation-signal
Draft

tisonkun wants to merge 1 commit into
mainfrom
codex/cancellation-signal

Conversation

@tisonkun

@tisonkun tisonkun commented Oct 3, 2026

Copy link
Copy Markdown
Member

Summary

Explore #223 with an opt-in cancellation API: a non-cloneable CancellationSource issues explicit stop requests, while cloneable CancellationToken observers query or await them. A runnable replica-lookup example cancels redundant work after the first answer and joins task handles separately.

This is a draft prototype for API discussion, not a decision to add another primitive. It includes seven behavior tests, feature wiring, API documentation, and a changelog entry.

Validation: cargo x test (626 tests), cargo x check (27 feature configurations), cargo x lint, cargo x build --locked, the cancel_lookups example, and the seven cancellation tests on Rust 1.86.0.

Design Notes

Contract. Only the source can cancel; sharing it with Arc explicitly delegates that authority. Requests are sticky and idempotent, including concurrent calls. Tokens support is_cancelled(), borrowed cancelled() waits, and cancelled_owned() futures that outlive the token. Dropping one wait removes only its registration. Cancellation requests neither interrupt work nor imply task completion.

Source drop. Dropping an uncancelled source does not cancel or wake observers. Their query stays false and existing or future waits remain pending indefinitely. This treats cancellation as an optional stop request raced with work that can finish normally. A request already issued survives source destruction. There is no separate closed state or abandonment error in this prototype.

Existing API Comparison
ShutdownWatch Already supports the signal, but constructing it also creates completion accounting, and awaiting its controller requests shutdown and joins that accounting. This prototype exposes only explicit requests and observation.
Arc<Latch> with count one Supplies the same synchronization. The new handles keep cancellation authority away from observers and give operation-level intent a name.
Completion<()> Also separates producer and observers, but consumes its producer on completion and reports Abandoned on producer drop. Prefer it when producer disappearance must wake observers.

Implementation. Both handles wrap the same Arc<Latch>; the feature enables latch. This reuses registration, cancellation cleanup, and wake handling without another state machine. A separate signal implementation would duplicate those mechanisms. Rebuilding shutdown on this API can wait until the public contract is accepted; its current implementation is unchanged.

Ecosystem. Tokio's token combines observing and cancelling in every clone. This proposal separates those capabilities. Explicit source destruction without a request also has precedent in .NET's CancellationTokenSource.Dispose; this does not adopt .NET's other disposal semantics.

Questions for review. Existing primitives can express this scenario: the benefit is a smaller, purpose-specific caller contract, not new coordination power. Is that worth two public types? Is permanently pending after source loss preferable to a distinct closure result? Is explicit Arc<CancellationSource> sufficient for multiple controllers? Hierarchy, cancellation reasons, signal composition, timers, and task ownership remain outside this draft.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant