Repository navigation
feat(wsl): let an idle WSL distro stop once its server idle-exits - #33084
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthrough
Priority: Merge Risk:
|
There was a problem hiding this comment.
Actionable comments posted: 2

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:
4c74efb3-59b8-4cd9-8499-b834d70b4fcb
Files selected for processing: 14-
.github/workflows/wsl-orcad-e2e.ymlsrc/main/orcad/orcad-managed-idle-exit-host.tssrc/main/orcad/orcad-wsl-holder-release.test.tssrc/main/orcad/orcad-wsl-holder-release.tssrc/main/wsl/wsl-distro-holder-command.tssrc/main/wsl/wsl-distro-holder-script.test.tssrc/main/wsl/wsl-distro-holder.test.tssrc/main/wsl/wsl-host-connector.tssrc/main/wsl/wsl-host-service.test.tssrc/main/wsl/wsl-host-service.tssrc/main/wsl/wsl-orcad-liveness-supervisor.test.tssrc/main/wsl/wsl-orcad-liveness-supervisor.tssrc/main/wsl/wsl-orcad-real-distro-idle-release-lane.test.tssrc/shared/orcad-idle-exit.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 3 remain after this review.
-
| 'p=; read -r p < "$f"', | ||
| "case $p in ''|*[!0-9]*) continue ;; esac", | ||
| 'while kill -0 "$p" 2>/dev/null; do sleep 1; done', | ||
| 'rm "$f" 2>/dev/null && exit 0', |
There was a problem hiding this comment.
🩺 Stability & Availability |
Do not remove a replacement release request.
If another Orcad replaces $f after this holder reads p, rm "$f" removes the new request and the holder exits without waiting for the new PID. A returning client could then have no holder keeping the distro running. Coordinate the claim with request publication, and exit only after confirming that the claimed request still belongs to p.
|
|
||
| createManager() | ||
| connection = await manager.ensure(PLAIN) | ||
| expect(connection.pid, 'the same orcad served the reconnect').not.toBeNull() |
There was a problem hiding this comment.
🎯 Functional Correctness |
Compare the reconnected Orcad PID with the original PID.
A restarted Orcad has a non-null PID, so this assertion passes even when the original server did not survive the live-terminal interval. Save connection.pid before disposal, require it to be non-null, and compare it with the PID after reconnection.
e0549f4 to
e371036
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

Minor · Ensure the holder before the non-ready return. · wsl-orcad-liveness-supervisor.ts:144-151
src/main/wsl/wsl-orcad-liveness-supervisor.ts:144-151
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winEnsure the holder before the non-ready return.
Deployment can adopt a holder that is still waiting for the previous Orcad to exit. That holder can exit before the new Orcad becomes ready. If
attachWatchreturns non-ready, this branch returns beforeensureHolderQuietly. Subsequentunverifiableretries only schedule another verification. The live Orcad can therefore remain without a holder, so the distro cannot idle-release until another path re-ensures it.Suggested fix
+ // A holder that took the previous orcad's idle release just before this launch is gone now. + await ensureHolderQuietly(this.deps.ensureHolder, entry.distro) if (!entry.watch && (await this.attachWatch(entry, connection)) !== 'ready') { if (entry.generation === generation) { this.scheduleVerify(entry) } return } - // A holder that took the previous orcad's idle release just before this launch is gone now. - await ensureHolderQuietly(this.deps.ensureHolder, entry.distro)

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:
665c8968-46a2-40e3-81d8-6174e7ed7d6f
Files selected for processing: 1-
src/main/wsl/wsl-orcad-real-distro-idle-release-lane.test.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 0 remain after this review.
-
70c1e5e to
b63447f
Compare
The WSL host's holder kept the distro (and the WSL VM) running after Orca quit, even with nothing running in it. orcad now leaves a release request in ~/.orca when a managed idle stop retired its terminal daemon; the holder waits for that orcad to exit, claims the request and ends, so WSL can stop the distro. Live terminals, working agents, enabled automations or a returning client keep the distro held as before. A host whose distro is stopped at startup reads as stopped instead of unreachable, and the real-WSL lane gains a phase that proves the release and records memory.
…the lane phase The real-WSL lane showed orcad idle-stopping with its daemon retired while the distro stayed up: the setsid'd orcad's environment is not guaranteed to carry WSL_DISTRO_NAME, so the release request was never written. Use ORCA_WSL_ORCAD_HOST, which only WSL-host launches set. Phase 3 now carries its phase name and path filter, and records what kept a distro running.
Each guest command left its exec loop running, a wsl.exe client that kept the distro up after the holder had already let go (the receipt showed no holder, no orcad, no request left).
3b7d5eb to
6c1de5b
Compare
…er trip on the request file Review of #33084: - The holder now checks, before claiming a release, that no process of the user runs outside the holder itself (session plumbing aside: systemd --user, (sd-pam), dbus-daemon). A detached tmux, nohup job or dev server keeps the distro; it fails toward holding. - A new holder drops any request left from before it started, so an older orcad's request can never end a holder a client just asked for. - orcad touches the request file only for WSL-host launches and never throws over it. - The real-WSL lane proves a detached job keeps the holder and its request, and the distro stops once the job ends.
Docker's containerd-shim processes run as root, so the user-only scan missed them and a release would have stopped running containers. Any containerd-shim process now keeps the hold.
…s the hold Delta review of #33084: dash reads a status file a byte at a time, so stop at Uid. Document that a root default user never releases, that per-user agents hold it, and the hidepid exception. The lane's pkill no longer matches the shell running it.
ELI5
When a WSL distro is added as an Orca host, Orca starts a small "holder" process that keeps the distro running, so terminals survive Orca quitting. Until now the holder never let go: the distro (and the whole WSL virtual machine, with its memory) stayed up forever, even when nothing was running in it. Now, once the Orca server inside the distro has been idle with no terminals, agents or automations, the holder steps aside and WSL can stop the distro like it does for any unused distro.
What Changed
Before: quit Orca with an empty WSL host and the distro stays Running indefinitely. The old WSL bridge let it stop.
After: the distro stops roughly 15 minutes (the server's existing idle timeout) plus a few seconds after the last Orca client leaves, as long as nothing is running in it. Anything live keeps it held exactly as before.
Mechanism (WSL hosts only, still behind
ORCA_INTERNAL_WSL_HOSTS):~/.orca/wsl-holder-release. The request is withdrawn if the stop turns out not to be idle (a signal or another stop took over) and whenever a new server starts.rm(an atomic claim: if a newly started server withdrew it first, the holder keeps holding) and exits. The script stays POSIX/busybox-safe."Idle" means nothing of the user's is running (review decision):
/procfor live processes of its own user outside the holder itself.systemd(user instance),(sd-pam),dbus-daemon, seeWSL_DISTRO_HOLDER_IGNORED_PROCESS_NAMES), as are zombies.nohup/setsidjob, a dev server or docker started from an Orca terminal. It fails toward keeping the distro up.containerd-shim*process, whoever owns it, because Docker's shims run as root. Root system services (dockerd with no containers, journald, cron) don't count..bashrc, a lingering editor server);/procis mountedhidepid, a root-owned container shim is invisible to the user, so it doesn't hold./proc/*/statusonly up to itsUid:line.Other safety points:
$HOME/.orca, while orcad uses the login shell's home (~/.orcafromresolveOrcadMachineHome). If they ever differ, or HOME is unset, the holder never finds a request and just keeps holding, so it is safe.ORCA_WSL_ORCAD_HOST), and a failure to remove it is logged, never thrown, at start or stop.arm). So a claim that races a new orcad start is caught then, not just by the 120 s heartbeat.Why
unverifiableorlive, it stays held. Losing contact is never treated as "nothing running".Wire compatibility: nothing new goes over RPC. A new desktop paired with an older server never sees a request, so it holds as today. An older holder ignores the file.
Builds on #34260 and #34307, both already on main. They stop the bash relay leaving a zombie and stop
wsl.exechannels from being killed mid-command. Without them, a leftover WSL session kept the distro up.Linked Issue
Internal WSL-via-orcad tracker R40 / E17.
Visual Proof
N/A. The only visible change is the existing "Distro stopped" row label showing at startup for a stopped distro. Behavior is proven by the real-WSL lane phase below.
Real-WSL result (head 3b7d5eb, which carried #34307 before it merged): the idle-release phase passed on ubuntu-24.04, debian-12 and fedora-41.
Testing
Unit:
orcad-wsl-holder-release.test.ts(writes only on a retired daemon inside WSL; not for live/unverifiable daemons, a returning client, outside WSL, or a taken-over stop; withdrawn at startup).wsl-distro-holder-script.test.tsruns the real script under/bin/sh: it holds with no request, waits for the pid, claims and exits 0, keeps holding if the request is withdrawn first, and ignores a garbage pid or a missing HOME. Supervisor and host-service tests cover the re-ensure and the startup status.Script portability: ran the script in alpine (busybox ash), debian (dash) and ubuntu containers: it holds, waits for orcad, then exits 0 and removes the request.
Real WSL: new Phase 3 in
wsl-orcad-e2e.yml. After the terminal closes, a detachedsetsid sleepmust keep the holder live and its request unclaimed past the idle window, and killing it lets the distro stop. Phase 3 (wsl-orcad-real-distro-idle-release-lane.test.ts). A fresh server with a 15 s idle timeout and one live terminal keeps the distro Running past the idle and poll windows. After the terminal closes and the client leaves, the distro stops with no release call. It records the seconds to stop plus guest and VM memory before and after.Automated tests added/updated
Notes
macOS/Linux are unaffected (no holder). SSH: the server writes the request only when
WSL_DISTRO_NAMEis set, and no SSH code reads it.Checklist
N/Afor visuals with reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (typecheck, oxlint and the changed-code gate pass locally; CI covers the rest)