mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
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:
@@ -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/'])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user