fix(rust): reap spawned process trees - #2292
Conversation
Bind each spawned CLI transport to an SDK-owned process tree before it can create descendants. Use a kill-on-close Job Object on Windows and a process group on Unix, and carry the RAII owner through startup, stop, force-stop, and drop paths. Cover grandchild teardown and startup-failure cleanup without changing the public client API. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1090dd5b-e149-4d9d-9cc3-67f26e11ad06
There was a problem hiding this comment.
Pull request overview
Adds cross-platform process-tree ownership and teardown for Rust SDK-spawned CLI processes.
Changes:
- Uses Unix process groups and Windows Job Objects.
- Integrates tree termination into startup, stop, force-stop, and drop paths.
- Adds lifecycle and process-tree tests.
Show a summary per file
| File | Description |
|---|---|
rust/src/process_tree.rs |
Implements process-tree lifecycle management. |
rust/src/lib.rs |
Integrates managed children into the client lifecycle. |
rust/src/errors.rs |
Updates teardown error documentation. |
rust/Cargo.toml |
Adds platform-specific dependencies. |
rust/Cargo.lock |
Locks the added dependencies. |
Review details
- Files reviewed: 4/5 changed files
- Comments generated: 3
- Review effort level: Balanced
| if reap_for(&mut child, SYNC_REAP_GRACE) { | ||
| return; | ||
| } |
| loop { | ||
| match child.try_wait() { | ||
| Ok(Some(_)) => break, | ||
| Ok(None) => std::thread::sleep(TREE_EXIT_POLL_INTERVAL), |
| impl StopErrors { | ||
| /// Borrow the collected errors as a slice, in the order they | ||
| /// occurred (per-session destroys first, then child-kill last). | ||
| /// occurred (per-session destroys first, then process-tree teardown). |
Match the repository's nightly rustfmt configuration on Linux. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1090dd5b-e149-4d9d-9cc3-67f26e11ad06
|
Spent time on this against github/app#2303. I think the fix is already in this diff — it's just not the headline change.
Could we land this PR as just those two changes plus their tests? That fixes #2303 on its own. On the process-tree work — I don't think it's wrong, but it's solving a different failure mode and I'd like to separate it. The issue reports "61 orphaned hosts with zero descendants," so descendant containment isn't what's broken here. The genuinely unique thing it buys is Windows
A few things worth carrying into that PR whenever it happens: the reaper thread in Last thing, and I think it's the highest-leverage one: should the runtime just exit on its own? The logs show Generated by Copilot |
Summary
KILL_ON_JOB_CLOSE, then resume its primary threadstop,force_stop, andDropFixes github/app#2303.
Validation
cargo test --all-features process_tree— 7 passedcargo test --all-features client_process_tree— 5 passedcargo +nightly-2026-04-14 fmt --checkcargo clippy --all-features --all-targets -- -D warningscargo +1.94.0 check --no-default-features --target x86_64-pc-windows-gnucargo test --all-features— complete suite passed, including 391 E2E tests with 3 ignoredConsumer sync note
Consumers that vendor the Rust SDK while retaining a consumer-owned
Cargo.tomlmust add the matching target dependencies after sync:libc = "0.2"on Unix andwindows-sys = "0.61"on Windows with the Foundation, Security, ToolHelp, JobObjects, and Threading feature sets used here. No public SDK API or call-site wiring changes are required.