Skip to content

fix: Clean up orphaned Windows child processes on shutdown#11829

Merged
anthonyshew merged 4 commits intomainfrom
shew/more-issue-11808
Feb 13, 2026
Merged

fix: Clean up orphaned Windows child processes on shutdown#11829
anthonyshew merged 4 commits intomainfrom
shew/more-issue-11808

Conversation

@anthonyshew
Copy link
Copy Markdown
Contributor

Summary

  • Fixes orphaned conhost.exe processes remaining after Ctrl+C on Windows (reported in pnpm dev hangs in turborepo-basic with turbo ^2.8.7 (TUI opens, no tasks start) #11808)
  • Adds per-child Windows Job Objects with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE so the entire process tree is terminated when a child handle is dropped
  • Adds a cross-platform test_process_tree_cleanup regression test that verifies grandchild processes are killed when the parent is stopped

Context

The primary issue from #11808 (ConPTY hanging due to PSEUDOCONSOLE_INHERIT_CURSOR) was fixed in #11816. However, a follow-up report confirmed that conhost.exe processes still remain orphaned after Ctrl+C on Windows.

The root cause: on Unix, setsid() creates a process group and kill(-pgid, SIGINT) kills the entire tree. On Windows, there was no equivalent — TerminateProcess only kills the direct child, leaving conhost.exe (spawned by ConPTY) and any grandchild processes alive.

How it works

Each spawned child process (both normal and PTY) is assigned to a Windows Job Object configured with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE. When the ChildHandle is dropped (after exit or kill), the job handle closes and Windows terminates all remaining processes in the job. This is the standard Windows mechanism for process tree management, analogous to Unix process groups.

Job Object creation failures are logged but non-fatal — the child still runs, just without the cleanup guarantee.

Testing

The new test_process_tree_cleanup test:

  1. Spawns a Node.js parent that itself creates a long-lived grandchild
  2. Asserts the grandchild is alive (pre-condition)
  3. Stops the parent
  4. Asserts the grandchild is dead

This test runs on both Unix (validating via setsid + kill(-pgid)) and Windows (validating via Job Objects). It needs to pass on Windows CI to fully confirm the fix.

…shutdown

On Windows, killing a process does not cascade to its children. When
using ConPTY (TUI mode), each PTY session spawns a conhost.exe process
that was left orphaned after Ctrl+C because TerminateProcess only kills
the direct child.

This adds a per-child Job Object configured with
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE so that when the job handle is
dropped (after the child exits or is killed), all processes in the tree
including conhost.exe and any grandchildren are terminated.

Closes #11808
@anthonyshew anthonyshew requested a review from a team as a code owner February 13, 2026 13:47
@anthonyshew anthonyshew requested review from tknickman and removed request for a team February 13, 2026 13:47
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Feb 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
examples-basic-web Ready Ready Preview, Comment, Open in v0 Feb 13, 2026 2:14pm
examples-designsystem-docs Ready Ready Preview, Comment, Open in v0 Feb 13, 2026 2:14pm
examples-gatsby-web Ready Ready Preview, Comment, Open in v0 Feb 13, 2026 2:14pm
examples-kitchensink-blog Ready Ready Preview, Comment, Open in v0 Feb 13, 2026 2:14pm
examples-nonmonorepo Ready Ready Preview, Comment, Open in v0 Feb 13, 2026 2:14pm
examples-svelte-web Ready Ready Preview, Comment, Open in v0 Feb 13, 2026 2:14pm
examples-tailwind-web Ready Ready Preview, Comment, Open in v0 Feb 13, 2026 2:14pm
examples-vite-web Ready Ready Preview, Comment, Open in v0 Feb 13, 2026 2:14pm
turbo-site Ready Ready Preview, Comment, Open in v0 Feb 13, 2026 2:14pm
turborepo-agents Ready Ready Preview, Comment, Open in v0 Feb 13, 2026 2:14pm
turborepo-test-coverage Ready Ready Preview, Comment, Open in v0 Feb 13, 2026 2:14pm

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented Feb 13, 2026

Congrats! CodSpeed is installed 🎉

🆕 4 new benchmarks were detected.

You will start to see performance impacts in the reports once the benchmarks are run from your default branch.

Detected benchmarks


Open in CodSpeed

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 13, 2026

Coverage Report

Metric Coverage
Lines 76.19%
Functions 47.11%
Branches 0.00%

View full report

@anthonyshew anthonyshew merged commit 93c5f10 into main Feb 13, 2026
102 of 105 checks passed
@anthonyshew anthonyshew deleted the shew/more-issue-11808 branch February 13, 2026 14:28
github-actions Bot added a commit that referenced this pull request Feb 13, 2026
## Release v2.8.8-canary.3

Versioned docs: https://v2-8-8-canary-3.turborepo.dev

### Changes

- release(turborepo): 2.8.8-canary.2 (#11832) (`8adf1e1`)
- fix: Harden token protection with `secrecy` crate and close exposure
gaps (#11831) (`200e4c3`)
- style: Fix oxfmt formatting in release workflow (#11833) (`d1f0b61`)
- fix: Clean up orphaned Windows child processes on shutdown (#11829)
(`93c5f10`)
- docs: Replace mysql refs with postgres (#11827) (`c992107`)

Co-authored-by: Turbobot <turbobot@vercel.com>
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.

1 participant