Repository navigation
Process.Kill can hang indefinitely on macOS arm64 #131944
Description
Activity
- addeduntriagedNew issue has not been triaged by the area ownerNew issue has not been triaged by the area owner
on Aug 6, 2026 dotnet-policy-service commented
on Aug 6, 2026 ContributorMore actionsTagging subscribers to this area: @dotnet/area-system-diagnostics-process
See info in area-owners.md if you want to be subscribed.Hi @mthalman
Thank you for a detailed bug report. I've hit hangs on macOS in #128598 but I was able to find the solution (#128598 (comment)) and get the dotnet/runtime CI green. Since then it never got stuck on macOS.
Were you able to collect a dump locally? Or at least attach a debugger and just check which methods exactly are blocked?
Do you happen to know ifdotnet-watch.Tests.dll.2does something unusual in terms of process management like p/invoking some native code that signs up for the SIGCHILD?I am asking these questions because I don't have an access to macOS arm64 machine (I will somehow try to get it, but it may take some time).
- added and removeduntriagedNew issue has not been triaged by the area ownerNew issue has not been triaged by the area owner
on Aug 11, 2026 This happens in Helix. I don't know much about Helix so I don't know if they capture a dump or not. But an example build is at https://dev.azure.com/dnceng-public/public/_build/results?buildId=1542039. The hang seems to be sporadic.
My mac is useless as I can't netiher build dotnet/runtime nor run the installed dotnet executable (it's simply too old and not supported). I've tried creating a standalone repro, but I've failed to reproduce the problem: https://github.com/adamsitnik/macosrepro
I suspect dotnet watch does something unusual that interferes with signal handling. I'll try to search for someone to try to debug it who has a working mac.
Hi @adamsitnik, I think I've hit the same thing (or a close cousin of it), and I've got a small standalone repro that fails reliably on GitHub-hosted macOS runners, so hopefully that helps since getting hold of a mac is the hard part 🙂
The twist on GitHub's runners is that it isn't just the call that blocks, the whole VM freezes. The runner loses contact with GitHub, step timeouts never fire, no logs get uploaded, and a root-owned process I left running as a canary stopped reporting at the same moment. Memory, network and file handles all looked healthy right up until it stopped.
Repro
repro.cs:using System.Diagnostics; for (var i = 1; i <= 200; i++) { using var p = Process.Start(new ProcessStartInfo("/bin/sh", ["-c", "sleep 60 & sleep 60; wait"]) { RedirectStandardOutput = true })!; Thread.Sleep(300); Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss.fff} iteration {i}: killing tree of {p.Id}"); p.Kill(entireProcessTree: true); // 💥 p.WaitForExit(); } Console.WriteLine("completed 200 iterations");
Workflow:
jobs: repro: strategy: matrix: os: [macos-26, macos-15] runs-on: ${{ matrix.os }} timeout-minutes: 4 steps: - uses: actions/checkout@v5 - uses: actions/setup-dotnet@v5 with: dotnet-version: 10.0.x - run: dotnet run repro.cs
Expected: 200 iterations, done in about a minute.
Actual: the job never finishes and gets killed at the job timeout. That happened 4/4 times (2×
macos-26, 2×macos-15, both arm64): https://github.com/slang25/glosharp/actions/runs/37140854273. It only takes the first few iterations, and the same loop runs all 200 iterations fine on my own Apple silicon mac (macOS 26), so it seems to be specific to the VMs.What I tried
I swapped
Kill(entireProcessTree: true)for other things in the same loop (https://github.com/slang25/glosharp/actions/runs/37138500101, https://github.com/slang25/glosharp/actions/runs/37139476351):Variant Froze Kill(entireProcessTree: true)5/5 Kill()0/3 just enumerating Process.GetProcesses()0/3 kill(SIGSTOP)thenkill(SIGKILL)via P/Invoke0/3 kill(SIGSTOP), enumerate all processes, thenkill(SIGKILL)0/3 find descendants from a ps -A -o pid=,ppid=snapshot, thenkill(SIGKILL)each0/3 So it seems to need the full recursive stop/enumerate/kill, which I couldn't reproduce by hand. One thing that might be relevant to the regression label: we first saw this with our test host on .NET 8 and the repro above runs on .NET 10, so I don't think it's only #128598. I could be wrong about that though, I haven't tried a .NET 11 build.
Images:
macos-26-arm6420260907.0351.1 (macOS 26.6.2) andmacos-15arm64.For now we've worked around it on macOS by killing the descendants from a
pssnapshot instead: slang25/glosharp#117. Happy to run anything else on those runners if it'd help.- added a commit that references this issue
on Oct 9, 2026
Description
On macOS arm64,
Process.Kill(entireProcessTree: true)can block indefinitely while terminating process trees created by thedotnet-watchtests.This appeared after consuming the runtime flow containing #128598, which introduced the two-phase stop-then-kill implementation on Unix.
Enable dotnet-watch tests on mac once fixed: dotnet/sdk#55679
Reproduction Steps
From a
dotnet/sdkcheckout at commitdc6cef6, run thedotnet-watch.Testssuite on macOS 15 arm64. In CI, this is Helix sharddotnet-watch.Tests.dll.2on queueosx.15.arm64.open.Allow tests to dispose their child processes using
Process.Kill(entireProcessTree: true).Observe that some calls never return.
A standalone runtime-only reproduction has not yet been reduced. The failure reproduces in the SDK CI shard linked below.
Expected behavior
Process.Kill(entireProcessTree: true)terminates the process tree and returns promptly.Actual behavior
Instrumentation logged nine calls entering
Process.Kill(entireProcessTree: true), but only three returned. Helix eventually detected the hang, collected dumps, and terminated the test process:One separate call returned from
Kill, but its process still did not exit within 30 seconds.Regression?
Observed after the SDK consumed the runtime flow containing #128598. That PR changed Unix process-tree termination to recursively stop the tree before killing it.
Confidence is high that the immediate hang occurs synchronously inside
Process.Kill(entireProcessTree: true). Confidence that #128598 introduced the regression is moderate pending a reduced runtime-only reproduction.Known Workarounds
None currently. Bounding the subsequent wait does not help because the call to
Process.Killitself does not return.Configuration
osx.15.arm64.opendotnet-watch.Tests.dll.2Other information