Re-enable Microsoft.Extensions.Hosting shutdown functional coverage in CI and harden shutdown startup/Helix/NET481 compatibility#128252
Conversation
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/f2762648-8aaf-44cd-a0bb-269b2a6b371d Co-authored-by: rosebyte <14963300+rosebyte@users.noreply.github.com>
|
@copilot , the shutdown tests are failing with this error on helix machines: Comments Error message Stack trace |
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/e8bb9c1b-1794-4ebc-a563-bec6823fec1c Co-authored-by: rosebyte <14963300+rosebyte@users.noreply.github.com>
... Fixed in b9cd702. I removed the source-tree lookup and switched the shutdown tests to run TestApp from the deployed test output ( |
|
@copilot , this time it failed with this error: Test Failures (700 tests failed) ❌ Microsoft.AspNetCore.Hosting.FunctionalTests.ShutdownTests.ShutdownTestRun [Console] [Details] [Artifacts] [0.70% failure rate] |
…fter Kill() Co-authored-by: rosebyte <14963300+rosebyte@users.noreply.github.com>
|
@copilot , you only addressed one of my comments, resolve all of them. |
… 180s for stress environments Co-authored-by: rosebyte <14963300+rosebyte@users.noreply.github.com>
Fixed in f9172b4. I've addressed both remaining issues:
|
| if (!process.HasExited) | ||
| { | ||
| process.Kill(); | ||
| // Wait for the process to actually exit after Kill() before accessing ExitCode | ||
| if (!process.WaitForExit(5000)) |
| if (!process.WaitForExit((int)timeout.TotalMilliseconds)) | ||
| { | ||
| throw new TimeoutException($"Process {processId} did not exit within the allotted timeout of {timeout}."); | ||
| } |
There was a problem hiding this comment.
Addressed in 16bc0339: KillProcessUnix no longer throws on SIGTERM timeout; it now escalates to Process.Kill() (hard kill) as best-effort cleanup instead of failing disposal.
| catch (InvalidOperationException) | ||
| { | ||
| // Ignore if process has already exited. | ||
| } | ||
| catch (Win32Exception) | ||
| { | ||
| // Ignore permission or process-not-found errors (e.g., kill not available). | ||
| } |
There was a problem hiding this comment.
Implemented in 16bc0339: Unix termination now uses libc kill(pid, SIGTERM) and falls back to Process.Kill() when graceful termination fails (including Win32Exception/error paths), so orphaned processes are still terminated.
| var psi = new ProcessStartInfo | ||
| { | ||
| FileName = "kill", | ||
| Arguments = $"-TERM {processId}", | ||
| RedirectStandardOutput = true, | ||
| RedirectStandardError = true, | ||
| UseShellExecute = false | ||
| }; |
There was a problem hiding this comment.
On .Net 11, you could use Process.GetProcessById(processId).SafeHandle.Signal(PosixSignal.SIGTERM). I think this only runs on Unix on .Net 11, so it could make sense to use that instead.
Or you could use the PInvoke approach from ShutdownTests. Both calls to kill should probably use the same approach.
There was a problem hiding this comment.
Implemented in 16bc033. ProcessExtensions.KillProcessUnix now uses libc kill(pid, SIGTERM) (matching the ShutdownTests signal approach), waits for exit, and falls back to Process.Kill() if graceful termination fails.
Co-authored-by: rosebyte <14963300+rosebyte@users.noreply.github.com>
| if (!process.WaitForExit((int)timeout.TotalMilliseconds)) | ||
| { | ||
| throw new TimeoutException($"Process {processId} did not exit within the allotted timeout of {timeout}."); | ||
| } |
| catch (InvalidOperationException) | ||
| { | ||
| // Ignore if process has already exited. | ||
| } | ||
| catch (Win32Exception) | ||
| { | ||
| // Ignore permission or process-not-found errors (e.g., kill not available). | ||
| } |
|
@copilot , I left comments for you, please implement them. |
Co-authored-by: rosebyte <14963300+rosebyte@users.noreply.github.com>
Fixed in |
main PR
This PR now includes the full follow-up hardening required by CI feedback after re-enabling
Microsoft.Extensions.Hostingfunctional shutdown coverage.Final scope of changes
AppContext.BaseDirectory) instead of source-tree project discovery.PreservePublishedApplicationForDebuggingdependency by using a borrowed published app path that does not delete test output on cleanup.killprocess usage in shutdown signaling withlibcsignal calls (SIGINT/SIGTERM) for Helix/minimal-image compatibility.SIGTERM)Process.Kill()) when neededDeployAsync()starts process output capture, so the initialStartedline cannot be missed.net481compatibility by usingMarshal.GetLastWin32Error().Validation
./build.sh clr+libs -rc releasesucceeds../build.sh clr+libs+host -rc release -lc releasesucceeds.Microsoft.Extensions.Hosting.Functional.Testsbuilds successfully fornet481.dotnet build /t:test Microsoft.Extensions.Hosting.Functional.Tests.csproj -c Release -f net11.0 /p:TestFilter=Microsoft.AspNetCore.Hosting.FunctionalTests.ShutdownTestspasses (2/2).