fix(wsl): re-read a distro's automount root after Orca wakes it, at most every 5 min (#36225)

* fix(wsl): re-read a distro's automount root after Orca wakes it, at most every 5 min

* test(wsl): pin that an unchanged automount re-read does not notify
This commit is contained in:
Neil
2026-10-29 23:02:13 -07:00
committed by GitHub
parent 511e4feca4
commit 480bcfc3ba
2 changed files with 99 additions and 6 deletions
+63 -1
View File
@@ -6,6 +6,7 @@ vi.mock('../wsl', () => ({ listRunningWslDistrosAsync: vi.fn(), listWslDistrosAs
import {
_resetWslBackgroundDistroGateForTests,
noteWslDistroMayHaveStopped,
noteWslDistroWoken,
WSL_STOP_SETTLE_MS
} from '../wsl-background-distro-gate'
import {
@@ -17,7 +18,8 @@ import {
knownWslAutomountRoots,
onWslAutomountRootsChanged,
parseWslAutomountRoot,
resetWslAutomountRootsForTests
resetWslAutomountRootsForTests,
WSL_AUTOMOUNT_WAKE_REREAD_INTERVAL_MS
} from './wsl-automount-root'
let now = 1_000
@@ -72,6 +74,7 @@ describe('followWslAutomountRoots', () => {
expect(changed).toHaveBeenCalledOnce()
await listRunning([])
expect(knownWslAutomountRoots()).toEqual(['/mnt/'])
now += WSL_STOP_SETTLE_MS
await listRunning(['Ubuntu'])
await vi.waitFor(() => expect(read).toHaveBeenCalledTimes(2))
})
@@ -120,6 +123,7 @@ describe('followWslAutomountRoots: one guest read per distro start', () => {
expect(reads).toBe(1)
await listRunning([])
now += WSL_STOP_SETTLE_MS
await observeRunning(10)
expect(reads).toBe(2)
}
@@ -158,7 +162,65 @@ describe('followWslAutomountRoots: one guest read per distro start', () => {
await listRunning([])
finishFirst('/mnt/')
await new Promise((resolve) => setImmediate(resolve))
now += WSL_STOP_SETTLE_MS
await observeRunning(3)
expect(reads).toBe(2)
})
// Terminals opened on a running distro each note a wake; only the interval lets one re-read through.
it('reads at most once more for many wakes while the distro runs', async () => {
let reads = 0
follow(async () => {
reads += 1
return '/mnt/'
})
await observeRunning(1)
expect(reads).toBe(1)
for (let i = 0; i < 10; i++) {
now += 1_000
noteWslDistroWoken('Ubuntu')
await observeRunning(1)
}
expect(reads).toBe(1)
now += WSL_AUTOMOUNT_WAKE_REREAD_INTERVAL_MS
for (let i = 0; i < 10; i++) {
noteWslDistroWoken('Ubuntu')
now += 1
await observeRunning(1)
}
expect(reads).toBe(2)
})
it('picks up a root edited across a restart no probe saw, once Orca wakes the distro', async () => {
let root = '/old/'
const changed = vi.fn()
onWslAutomountRootsChanged(changed)
follow(async () => root)
await observeRunning(1)
expect(knownWslAutomountRoots()).toEqual(['/mnt/', '/old/'])
// An unchanged re-read leaves listeners (the git router's cache) alone.
now += WSL_AUTOMOUNT_WAKE_REREAD_INTERVAL_MS
noteWslDistroWoken('Ubuntu')
now += 1
await observeRunning(1)
expect(changed).toHaveBeenCalledOnce()
// Stopped and started again between probes, wsl.conf edited meanwhile.
root = '/new/'
now += WSL_AUTOMOUNT_WAKE_REREAD_INTERVAL_MS
noteWslDistroWoken('Ubuntu')
now += 1
await observeRunning(1)
expect(knownWslAutomountRoots()).toEqual(['/mnt/', '/new/'])
root = '/mnt/'
now += WSL_AUTOMOUNT_WAKE_REREAD_INTERVAL_MS
noteWslDistroWoken('Ubuntu')
now += 1
await observeRunning(1)
expect(knownWslAutomountRoots()).toEqual(['/mnt/'])
})
})
+36 -5
View File
@@ -2,12 +2,17 @@
* Where each WSL project's distro mounts Windows drives (`[automount] root`, `/mnt/` by default),
* so a worktree link WSL Git wrote as `<root>c/src/app/.git/worktrees/x` maps back to `C:\…`.
* Read only from a distro confirmed running after any stop settled, never waking a stopped one,
* and read again after it stops, since `wsl.conf` changes take effect on the next start.
* and read again after it stops, since `wsl.conf` changes take effect on the next start. A stop and
* restart between two probes goes unseen, so Orca's own wake also allows one re-read; a restart
* outside Orca with no wake after it keeps the old root until the next noticed stop or wake.
*/
import { runProcess } from '@orca/process-host'
import { buildPosixStdoutFence } from '../../shared/posix-stdout-fence'
import { buildWslExecArgs } from '../../shared/wsl-login-shell-command'
import { wasWslDistroLastSeenRunningSettled } from '../wsl-background-distro-gate'
import {
onWslDistroGateNote,
wasWslDistroLastSeenRunningSettled
} from '../wsl-background-distro-gate'
import { resolveWslInteropSpawnCwd } from '../wsl-interop-spawn-directory'
import {
onConfirmedRunningWslDistros,
@@ -18,12 +23,15 @@ import { resolveWslExecutablePath } from './wsl-executable-path'
export const DEFAULT_WSL_AUTOMOUNT_ROOT = '/mnt/'
const READ_TIMEOUT_MS = 5_000
// A wake re-reads at most this often per distro, so terminals opened on a running distro stay free.
export const WSL_AUTOMOUNT_WAKE_REREAD_INTERVAL_MS = 5 * 60_000
export type WslAutomountRootReader = (distro: string) => Promise<string | null>
const roots = new Map<string, string>()
// Read since its last start, the default `/mnt/` too: one guest launch per start, not per probe.
const readSinceStart = new Set<string>()
const readAt = new Map<string, number>()
const pending = new Set<string>()
// Bumped on a stop, so a read still in flight then neither marks the restarted distro read.
const stopCounts = new Map<string, number>()
@@ -78,10 +86,17 @@ export function followWslAutomountRoots(
return
}
readSinceStart.add(key)
if (root !== DEFAULT_WSL_AUTOMOUNT_ROOT) {
roots.set(key, root)
changed()
readAt.set(key, performance.now())
const previous = roots.get(key) ?? DEFAULT_WSL_AUTOMOUNT_ROOT
if (root === previous) {
return
}
if (root === DEFAULT_WSL_AUTOMOUNT_ROOT) {
roots.delete(key)
} else {
roots.set(key, root)
}
changed()
})
// Unreadable: links under it fall back to `/mnt/`.
.catch(() => {})
@@ -94,15 +109,30 @@ export function followWslAutomountRoots(
const key = distroKey(distro)
stopCounts.set(key, (stopCounts.get(key) ?? 0) + 1)
readSinceStart.delete(key)
readAt.delete(key)
removed = roots.delete(key) || removed
}
if (removed) {
changed()
}
})
// A wake may have restarted it unseen: the next settled probe reads again, its old root kept until then.
const stopWoken = onWslDistroGateNote((note) => {
const key = distroKey(note.distro)
const lastRead = readAt.get(key)
if (
note.kind === 'woken' &&
readSinceStart.has(key) &&
lastRead !== undefined &&
performance.now() - lastRead >= WSL_AUTOMOUNT_WAKE_REREAD_INTERVAL_MS
) {
readSinceStart.delete(key)
}
})
return () => {
stopRunning()
stopStopped()
stopWoken()
}
}
@@ -146,6 +176,7 @@ export function parseWslAutomountRoot(drivePath: string | null): string | null {
export function resetWslAutomountRootsForTests(): void {
roots.clear()
readSinceStart.clear()
readAt.clear()
pending.clear()
stopCounts.clear()
changeListeners.clear()