util: Use job objects to reap spawned process trees on Windows - #58885
Merged
Conversation
miguelraz
marked this pull request as draft
June 9, 2026 01:51
miguelraz
force-pushed
the
windows-job-objects
branch
from
June 9, 2026 02:14
20079f8 to
9be0c01
Compare
On Windows, util::process::Child only killed the direct child, so descendants spawned by ACP agent servers (npx workers, MCP servers) were orphaned and accumulated indefinitely. Assign spawned processes to a job object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE so the whole tree is terminated on kill, on drop, and when Zed exits. Closes #58873
miguelraz
force-pushed
the
windows-job-objects
branch
from
June 9, 2026 02:20
9be0c01 to
2e8f3fc
Compare
smitbarmase
marked this pull request as ready for review
June 9, 2026 14:22
Member
|
Okay here I got the reproduction:
"agent_servers": {
"Claude Code (leak repro)": {
"command": "npx",
"args": ["-y", "@zed-industries/claude-code-acp@0.16.2"],
"env": {}
}
}
{ "mcpServers": { "filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "C:\\Users\\heysm"] } } }
while ($true) { Clear-Host; Get-CimInstance Win32_Process -Filter "Name='node.exe'" |
Where-Object { $_.CommandLine -match 'claude-code-acp|claude-agent-sdk|server-filesystem' } |
Select ProcessId, CommandLine | Format-List; Start-Sleep 2 }
|
Member
|
Seems reasonable, but I don't have a Windows machine to test nor knowledge of the APIs. Would be good if someone that worked on the Windows port looks at this 😄 |
This was referenced Jun 18, 2026
Closed
This was referenced Jul 1, 2026
This was referenced Jul 10, 2026
jolutz
pushed a commit
to jolutz/zed
that referenced
this pull request
Aug 8, 2026
…ndustries#58885) On Windows, `util::process::Child::kill()` only terminated the direct child process, and nothing tied the lifetime of spawned process trees to Zed. External agent servers launched through ACP (e.g. `claude-code-acp` via `npx`) spawn node workers and MCP servers as grandchildren, which were orphaned on every session teardown and accumulated indefinitely — hundreds of idle `node.exe` processes and GBs of RAM over days. This PR assigns spawned processes to a Win32 job object configured with `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`: - `kill()` now calls `TerminateJobObject`, killing the entire tree instead of just the (shell wrapper) child. - Dropping `Child` closes the job handle, which makes the OS reap the tree — including when Zed exits for any reason (even crashes), since the OS closes its handles. - Unix behavior (process groups via `killpg`) is unchanged. Added two Windows tests that spawn a real `powershell -> ping` process tree and assert the grandchild is terminated on `kill()` and on drop. Both fail without the fix and pass with it. Verified with `cargo test -p util`, `script/clippy -p util`, and `cargo check -p agent_servers -p dap` on Windows 11. Closes zed-industries#58873 Release Notes: - Fixed external agent servers and debug adapters leaking helper processes (e.g. node workers and MCP servers) on Windows. --------- Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On Windows,
util::process::Child::kill()only terminated the direct child process, and nothing tied the lifetime of spawned process trees to Zed. External agent servers launched through ACP (e.g.claude-code-acpvianpx) spawn node workers and MCP servers as grandchildren, which were orphaned on every session teardown and accumulated indefinitely — hundreds of idlenode.exeprocesses and GBs of RAM over days.This PR assigns spawned processes to a Win32 job object configured with
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE:kill()now callsTerminateJobObject, killing the entire tree instead of just the (shell wrapper) child.Childcloses the job handle, which makes the OS reap the tree — including when Zed exits for any reason (even crashes), since the OS closes its handles.killpg) is unchanged.Added two Windows tests that spawn a real
powershell -> pingprocess tree and assert the grandchild is terminated onkill()and on drop. Both fail without the fix and pass with it. Verified withcargo test -p util,script/clippy -p util, andcargo check -p agent_servers -p dapon Windows 11.Closes #58873
Release Notes: