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. --------- Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
69 lines
1.5 KiB
TOML
69 lines
1.5 KiB
TOML
[package]
|
|
name = "util"
|
|
version = "0.1.0"
|
|
edition.workspace = true
|
|
publish = false
|
|
license = "Apache-2.0"
|
|
description = "A collection of utility structs and functions used by Zed and GPUI"
|
|
|
|
|
|
[lints]
|
|
workspace = true
|
|
|
|
[lib]
|
|
path = "src/util.rs"
|
|
doctest = true
|
|
|
|
[features]
|
|
test-support = ["rand", "util_macros"]
|
|
|
|
[dependencies]
|
|
anyhow.workspace = true
|
|
async_zip.workspace = true
|
|
collections.workspace = true
|
|
dunce.workspace = true
|
|
futures-lite.workspace = true
|
|
futures.workspace = true
|
|
globset.workspace = true
|
|
itertools.workspace = true
|
|
log.workspace = true
|
|
rand = { workspace = true, optional = true }
|
|
regex.workspace = true
|
|
rust-embed.workspace = true
|
|
schemars.workspace = true
|
|
serde.workspace = true
|
|
serde_json.workspace = true
|
|
serde_json_lenient.workspace = true
|
|
shlex.workspace = true
|
|
take-until.workspace = true
|
|
tempfile.workspace = true
|
|
unicase.workspace = true
|
|
url.workspace = true
|
|
percent-encoding.workspace = true
|
|
util_macros = { workspace = true, optional = true }
|
|
gpui_util.workspace = true
|
|
|
|
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
|
smol.workspace = true
|
|
which.workspace = true
|
|
async-fs.workspace = true
|
|
walkdir.workspace = true
|
|
dirs.workspace = true
|
|
|
|
[target.'cfg(unix)'.dependencies]
|
|
command-fds = "0.3.1"
|
|
libc.workspace = true
|
|
nix = { workspace = true, features = ["user"] }
|
|
|
|
[target.'cfg(target_os = "macos")'.dependencies]
|
|
mach2.workspace = true
|
|
|
|
[target.'cfg(windows)'.dependencies]
|
|
tendril = "0.4.3"
|
|
windows.workspace = true
|
|
|
|
[dev-dependencies]
|
|
rand.workspace = true
|
|
util_macros.workspace = true
|
|
pretty_assertions.workspace = true
|