Fix CLI cancellation disposal race on shutdown#17179
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17179Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17179" |
There was a problem hiding this comment.
Pull request overview
Fixes an Aspire CLI shutdown race where Ctrl+C/SIGTERM callbacks can attempt to cancel a CancellationTokenSource after it has been disposed, causing ObjectDisposedException during teardown.
Changes:
- Switches Ctrl+C and SIGTERM handling to use removable handlers and a shared
TryCancelhelper. - Guards late-signal cancellation with an
ObjectDisposedExceptioncatch to avoid process-aborting crashes. - Explicitly unregisters signal handlers and disposes signal registrations/CTS during shutdown.
Ctrl+C/SIGTERM handlers and CancellationTokenSource were only cleaned up in a finally block after command invocation. If BuildApplicationAsync or StartAsync failed, the early catch returned without reaching that finally, leaking the handlers. Introduce ConsoleCancellationManager (IDisposable) that encapsulates signal registration and CTS lifetime. A using declaration at the top of Main guarantees cleanup on all exit paths. The CancellationToken is captured at construction so it remains usable after disposal.
|
Re-running the failed jobs in the CI workflow for this pull request because 1 job was identified as retry-safe transient failures in the CI run attempt.
Matched test failure patterns (1 test)
|
|
❌ CLI E2E Tests failed — 84 passed, 1 failed, 1 unknown (commit Failed Tests
View all recordings
📹 Recordings uploaded automatically from CI run #25975341405 |
|
✅ No documentation update needed. docs_required → already documented by name Triggered signals (1): Evidence: The signal fired on
The PR itself ( |
Description
Fixes a shutdown race in the Aspire CLI where signal callbacks can call
CancellationTokenSource.Cancel()after the source is disposed, causing:System.ObjectDisposedException: The CancellationTokenSource has been disposed.For example: #17141 (comment)
The change in src/Aspire.Cli/Program.cs updates startup cancellation wiring to:
ObjectDisposedExceptionin that helper for late-signal races.This keeps shutdown cancellation behavior intact while preventing process aborts from late signal delivery during teardown.
Validation run:
dotnet test --project .\tests\Aspire.Cli.Tests\Aspire.Cli.Tests.csproj --no-launch-profile -- --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"(passed: 3073, skipped: 12)dotnet test --project .\tests\Aspire.Cli.EndToEnd.Tests\Aspire.Cli.EndToEnd.Tests.csproj --no-launch-profile -- --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"(failed in local environment because Docker daemon pipe//./pipe/dockerDesktopLinuxEnginewas unavailable)Fixes # (issue)
Checklist
<remarks />and<code />elements on your triple slash comments?