Skip to content

fix(wsl): close a WSL host channel with stdin EOF before killing wsl.exe - #34307

Merged
nwparker merged 2 commits into
mainfrom
nwparker/wsl-channel-graceful-close
Oct 27, 2026
Merged

nwparker merged 2 commits into
mainfrom
nwparker/wsl-channel-graceful-close

Conversation

@nwparker

@nwparker nwparker commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​95 $\color{#cf222e}{\Huge{\mathbf{−}}}$​8 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​87
Prod 2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​39 $\color{#cf222e}{\Huge{\mathbf{−}}}$​2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​37

ELI5

When Orca finishes talking to a WSL host over a wsl.exe channel, it used to kill wsl.exe outright. WSL sometimes leaves that session behind when that happens, and a leftover session keeps the distro, and its virtual machine, running. Now Orca first tells the command it's done (closes its input) and only kills wsl.exe if the command hasn't exited within 2 seconds.

What Changed

Before: RemoteCommandChannel.close() on a WSL host channel called child.kill() on wsl.exe at once. That covers stdio bridges, liveness watches, and probes that are cancelled or time out. Seen after an idle stop: Orca had nothing left in the distro, yet session /init processes with no children stayed, each matched by a lingering wslhost.exe --distro-id … on Windows. The distro never stopped.

After: close() ends wsl.exe's stdin first. Every guest command Orca runs on these channels exits on EOF: the bash relay's stdin cat, the Node bridge, and the command loop's read. So the guest exits by itself, wsl.exe exits normally, and WSL ends the session. If the guest is still running after WSL_CHANNEL_CLOSE_GRACE_MS (2 s), wsl.exe is killed as before. The timer is unref'd and cleared on exit.

Why

What proved it: #33084's real-WSL idle-release phase. A fresh orcad with a 15 s idle timeout; a live terminal must keep the distro up; then the last client leaves and the distro must stop with no release call.

  • Before this change: 3/3 distros (ubuntu-24.04, debian-12, fedora-41) failed. The distro was still running after 120 s, with childless session /inits and matching wslhost.exe helpers.
  • With it: 3/3 passed. Each distro stopped 37–38 s after the last client, and the WSL VM process went from about 1.2–1.3 GB to gone.

I didn't pin which single channel left each session: on the passing run the failure diagnostics never fired. The fix applies to every close on these channels.

Alternatives: we can't kill WSL's session init from our side, and a longer or shorter grace changes nothing for guests that exit on EOF.

Flag-off: only WSL-host channels use this path. Its single caller is createWslOrcadHostTransport's exec, which is used only inside src/main/wsl/ (WSL host connection, deploy, liveness, pairing repair, decommission), behind ORCA_INTERNAL_WSL_HOSTS. With the flag off it never runs. The older WSL bridge (terminals, git, CLI, hook relay) and SSH use other code, so app quit, terminal close and cancelled git operations are untouched.

Linked Issue

Internal WSL-via-orcad tracker R40 / E17 (needed by #33084).

Visual Proof

N/A: there's no UI change. This is process teardown inside WSL.

Testing

wsl-command-channel.test.ts (new) checks three things:

  • close ends stdin first, so a guest that exits on EOF finishes with no kill;
  • a guest that ignores EOF is killed after exactly the grace period;
  • close after exit does nothing.

The WSL transport, stdio bridge, watch and connection suites pass. The real-WSL proof is #33084's lane, which carries this commit until it lands.

  • Automated tests added/updated

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • N/A for visuals with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote impact considered
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (typecheck, the touched tests and the changed-code gate pass locally; CI runs the rest)

…sl.exe

Killing wsl.exe while its guest command still runs can leave the WSL session (an /init with a
wslhost.exe helper) behind, which keeps an idle distro running. A closed channel now ends stdin
first and kills wsl.exe only if the guest has not exited within 2 s.
@coderabbitai

coderabbitai Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: dc219e81-98fc-476b-979c-407b227ac85d
Commits

Reviewing files that changed from the base of the PR and between 9fbd3ae and ab9ec10.

Files selected for processing: 4
  • src/main/startup/main-process-quit.ts
  • src/main/wsl/wsl-command-channel.test.ts
  • src/main/wsl/wsl-command-channel.ts
  • src/main/wsl/wsl-orcad-host-transport.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 2 remain after this review.

Walkthrough

The WSL command channel now ends stdin when closing a running child process and waits up to 2,000 ms before killing it. If the child exits during that period, the timer is cleared. If the child has already exited when close() runs, the method returns without killing it. At app quit, the handler kills channels that remain in the close grace period. Tests cover these shutdown cases and the abort path.

Priority: Normal

Merge Risk: Minimal · up to ab9ec

No actionable merge-blocking risk is established for the WSL channel shutdown change; it is ready to merge after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surv… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title "fix(wsl): close a WSL host channel with stdin EOF before killing wsl.exe" matches the main change. The PR closes stdin on wsl.exe first and kills the process only after a 2-second grace per…
Description check Passed The description follows the repository template closely. It has ELI5, What Changed, Why, Linked Issue, Visual Proof, Testing, and Checklist sections. It explains the before and after behavior, the mec…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title "fix(wsl): close a WSL host channel with stdin EOF before killing wsl.exe" matches the main change. The PR closes stdin on wsl.exe first and kills the process only after a 2-second grace period. The title is specific and concise, and it follows a conventional commit style. It does not mention the follow-up quit-time kill, but that is a secondary part of the change.

Full details: Description check

Explanation

The description follows the repository template closely. It has ELI5, What Changed, Why, Linked Issue, Visual Proof, Testing, and Checklist sections. It explains the before and after behavior, the mechanism (stdin EOF, then kill after WSL_CHANNEL_CLOSE_GRACE_MS of 2 s), and the alternatives considered. It also states that visuals are N/A with a reason. Gaps: the Linked Issue section lists an internal tracker (R40 / E17) and no 'Fixes #' issue, which the template says outside contributors must provide. The template's AI Disclosure, Review, and Agent skill upstream boundary sections are absent. The Testing section lists only the automated tests box, and the manual-testing box is not checked. The lint/typecheck/test/build checklist item is unchecked, with a note that CI covers the rest. The description does not mention the quit-time kill (killWslChannelsInCloseGrace) that the commit messages describe.

Resolution

Consider these improvements before merge: (1) Confirm the Linked Issue is acceptable. The template asks for a 'Fixes #' issue reference, and the description only names an internal tracker. (2) Add a line for the app-quit path (killWslChannelsInCloseGrace), which the objectives describe as a follow-up commit. (3) Complete or explain the AI Disclosure and Agent skill upstream boundary sections. (4) State whether the change was tested manually on a platform, or leave the box unchecked with a reason.

Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 4 functions across 4 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Review of #34307: a guest command that ignores stdin EOF would otherwise keep its wsl.exe up
past Orca's exit for the rest of the grace. Quit teardown now kills every channel still in its
grace. The abort test runs on fake timers instead of waiting the grace in real time.
@nwparker
nwparker merged commit e59e4eb into main Oct 27, 2026
47 checks passed
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