Skip to content

Kill descendants in Process.Kill(entireProcessTree) polyfill - #657

Merged
SimonCropp merged 2 commits into
mainfrom
fix/kill-entire-process-tree
Oct 11, 2026
Merged

SimonCropp merged 2 commits into
mainfrom
fix/kill-entire-process-tree

Conversation

@SimonCropp

Copy link
Copy Markdown
Owner

Bug

The Process.Kill(bool entireProcessTree) polyfill (net4x, netstandard, netcoreapp2.x, uap) ignored its argument and called Process.Kill(), so Kill(entireProcessTree: true) left every child process running. It also threw InvalidOperationException for a process that had already exited, where the BCL returns.

Fix

Kill(true) now goes through a nested ProcessTreeHelper:

  • Windows: follows the BCL approach. Each process is killed first (so it cannot start more children) and its children are then found by parent process id from a Toolhelp snapshot, recursively. Because a parent id can outlive its process and be reused, a child is only accepted when it started after its parent, and a process whose start time cannot be read has no children killed.
  • Unix (best effort): children are reparented when their parent exits, so the descendants are collected first from ps -A -o pid=,ppid=, then the root and each descendant are sent SIGKILL. A child started between the listing and the kill is missed, and if ps cannot be run only the root is killed. This is recorded as a //Note: in the API list.
  • As in the BCL, an already exited process is a no-op, a tree containing the calling process throws InvalidOperationException, and failures to terminate are collected into an AggregateException.

The Unix path was not executed: I only had Windows available, and the repo's tests run on Windows CI.

Tests

  • Process_Kill_EntireProcessTree_KillsDescendants starts a shell (cmd /c ping -n 60 on Windows, sh -c with sleep 60 elsewhere) whose child inherits the redirected output pipe, kills the tree, and asserts the output reaches its end within 20 seconds, which only happens once the child has exited too.
  • Process_Kill_EntireProcessTree_Exited kills the tree of an exited process.

Both fail on net48 without the fix and pass on net48 and net10.0 (BCL). The whole Process* test group passes on net48.

@SimonCropp SimonCropp added this to the 11.4.4 milestone Oct 11, 2026
@SimonCropp
SimonCropp merged commit 0df2a74 into main Oct 11, 2026
3 checks passed
@SimonCropp
SimonCropp deleted the fix/kill-entire-process-tree branch October 11, 2026 22:11
This was referenced Oct 12, 2026
This was referenced Oct 13, 2026
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.

2 participants