Skip to content

[Microsoft.Android.Run] Await app shutdown on Ctrl+C - #12318

Merged
jonathanpeppers merged 5 commits into
dotnet:mainfrom
jonathanpeppers:jonathanpeppers-fix-run-cancellation
Aug 11, 2026
Merged

[Microsoft.Android.Run] Await app shutdown on Ctrl+C#12318
jonathanpeppers merged 5 commits into
dotnet:mainfrom
jonathanpeppers:jonathanpeppers-fix-run-cancellation

Conversation

@jonathanpeppers

Copy link
Copy Markdown
Member

Pressing Ctrl+C during dotnet run could let Microsoft.Android.Run exit before its fire-and-forget adb shell am force-stop completed, leaving the application running on the device.

Move shutdown into the awaited run lifecycle. The Ctrl+C handler now only signals cancellation, while final cleanup waits for force-stop before returning the standard SIGINT exit code. The device integration test now requires the app to be stopped immediately when dotnet run exits instead of polling around the race.

Fixes #11264

  • Useful description of why the change is necessary.
  • Links to issues fixed
  • Unit tests: strengthened DotNetRunCtrlC; Microsoft.Android.Run builds with 0 warnings and errors.

jonathanpeppers and others added 2 commits August 7, 2026 14:38
Ensure Ctrl+C waits for adb force-stop before the run tool exits, while preserving the standard cancellation exit code. Strengthen the device test to require shutdown completion without polling.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace duplicated magic values with a documented constant.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 7, 2026 19:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a race where Microsoft.Android.Run could exit on Ctrl+C before adb shell am force-stop completed, leaving the app running on the device. It moves shutdown into the awaited run lifecycle and tightens the device integration test to require the app to be stopped when dotnet run exits.

Changes:

  • Await StopAppAsync() during the run lifecycle shutdown instead of fire-and-forget from the Ctrl+C handler.
  • Standardize SIGINT exit code handling via a CtrlCExitCode constant and improve StopAppAsync() error reporting.
  • Strengthen DotNetRunCtrlC device integration test to fail if cancellation is reported as an error and to require immediate app shutdown.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Microsoft.Android.Run/Program.cs Moves Ctrl+C shutdown work into the awaited lifecycle and adjusts stop-app behavior/exit code handling.
tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs Updates Ctrl+C test expectations to validate no cancellation error and immediate device app termination.

Comment thread src/Microsoft.Android.Run/Program.cs Outdated
Track Ctrl+C independently from the cancellation token source because instrumentation also cancels that source during normal cleanup.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 80fed9de-25fd-4f10-9c83-145a24798a3c
@jonathanpeppers

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Needs Changes — 0 errors, 1 warning, 1 suggestion.

The cleanup is now correctly awaited before exit, and the immediate device-state assertion is stronger. The awaited adb force-stop still needs a bounded shutdown path so an unresponsive adb cannot make Ctrl+C hang indefinitely; the regression test should also lock in exit code 130.

CI is still in progress; all completed checks currently pass.

Generated by Android PR Reviewer for #12318 · gpt56 · 160.9 AIC · ⌖ 10.1 AIC · ⊞ 25.3K
Comment /review to run again

Comment thread src/Microsoft.Android.Run/Program.cs Outdated
Comment thread tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs
@jonathanpeppers jonathanpeppers added ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). and removed ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). labels Aug 10, 2026
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@jonathanpeppers jonathanpeppers added the ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). label Aug 10, 2026
@jonathanpeppers

Copy link
Copy Markdown
Member Author

@dalexsoto review

@jonathanpeppers
jonathanpeppers enabled auto-merge (squash) August 11, 2026 12:20

@dalexsoto dalexsoto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking on one cancellation cleanup regression in src/Microsoft.Android.Run/Program.cs (around lines 211-219 and 577-608). If SIGINT arrives while the app-liveness pidof command is running, cancellation bypasses logcat cleanup. Since the Ctrl+C handler no longer kills logcat, its adb process can survive after Microsoft.Android.Run exits. This was reproduced at the current head: force-stop completed and exit code 130 was returned, but logcat remained reparented to PID 1. Please make logcat cleanup unconditional (for example, lifecycle cleanup in finally) and cover cancellation while pidof is in flight.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@jonathanpeppers

Copy link
Copy Markdown
Member Author

@dalexsoto Fixed in 53688aed6. WaitForAppExitAsync() now wraps the full PID/logcat polling lifecycle in try/finally, so cancellation during an in-flight pidof still kills and waits for the logcat process before unwinding. The Ctrl+C handler remains synchronous. The run tool builds cleanly; I did not add a timing-dependent device test because there is currently no deterministic seam for holding pidof in flight.

@dalexsoto dalexsoto left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the current changes. The new try/finally closes the in-flight GetAppPidAsync() cleanup hole: logcat is always killed and awaited before unwinding, followed by bounded, awaited force-stop cleanup. No blocking issues found.

@jonathanpeppers
jonathanpeppers merged commit 525501c into dotnet:main Aug 11, 2026
44 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dotnet run Ctrl+C does not stop the app on the device

3 participants