Skip to content

util: Use job objects to reap spawned process trees on Windows - #58885

Merged
Veykril merged 4 commits into
mainfrom
windows-job-objects
Jun 15, 2026
Merged

util: Use job objects to reap spawned process trees on Windows#58885
Veykril merged 4 commits into
mainfrom
windows-job-objects

Conversation

@miguelraz

Copy link
Copy Markdown
Contributor

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 #58873

Release Notes:

  • Fixed external agent servers and debug adapters leaking helper processes (e.g. node workers and MCP servers) on Windows.

@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Jun 9, 2026
@zed-community-bot zed-community-bot Bot added the staff Pull requests authored by a current member of Zed staff label Jun 9, 2026
@miguelraz
miguelraz marked this pull request as draft June 9, 2026 01:51
@miguelraz
miguelraz force-pushed the windows-job-objects branch from 20079f8 to 9be0c01 Compare June 9, 2026 02:14
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
miguelraz force-pushed the windows-job-objects branch from 9be0c01 to 2e8f3fc Compare June 9, 2026 02:20
@smitbarmase
smitbarmase marked this pull request as ready for review June 9, 2026 14:22
@smitbarmase

Copy link
Copy Markdown
Member

Okay here I got the reproduction:

  1. settings.json - add a leaking agent (the modern default self cleans, so you must force the old adapter):
"agent_servers": {
  "Claude Code (leak repro)": {
    "command": "npx",
    "args": ["-y", "@zed-industries/claude-code-acp@0.16.2"],
    "env": {}
  }
}
  1. Also have some mcp server by having .mcp.json in repo root:
{ "mcpServers": { "filesystem": {
  "command": "npx",
  "args": ["-y", "@modelcontextprotocol/server-filesystem", "C:\\Users\\heysm"] } } }
  1. Monitor command (separate PowerShell, leave running):
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 }
  1. Steps: Open Zed → Agent panel → pick "Claude Code (leak repro)" → send one message → confirm the tree spawns (npx → …claude-code-acp\dist\index.js → …claude-agent-sdk\cli.js → …server-filesystem) → quit Zed.

  2. After close, check if you still have running processes. With above setup you should see leaky processes, while with this fix, Zed cleans it up and does not depend on external process for that clean up.

@smitbarmase
smitbarmase requested a review from bennetbo June 10, 2026 08:38
@bennetbo

Copy link
Copy Markdown
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 😄

@Veykril Veykril 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.

Seems correct to me

@Veykril
Veykril added this pull request to the merge queue Jun 15, 2026
Merged via the queue into main with commit c642b42 Jun 15, 2026
33 checks passed
@Veykril
Veykril deleted the windows-job-objects branch June 15, 2026 10:58
This was referenced Jun 18, 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement staff Pull requests authored by a current member of Zed staff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows: claude-code-acp orphans node/MCP helper processes that are never reaped, accumulating to hundreds of processes / GBs over days

4 participants