From 8fdffc6bf6864a5980d4a4bee5187354fe6b5bf5 Mon Sep 17 00:00:00 2001 From: Orca Worker Date: Fri, 11 Sep 2026 21:35:47 -0700 Subject: [PATCH] perf(wsl): release environment probe deadline timers --- src/main/wsl/wsl-guest-environment.test.ts | 27 ++++++++++++++++++++++ src/main/wsl/wsl-guest-environment.ts | 9 ++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/main/wsl/wsl-guest-environment.test.ts b/src/main/wsl/wsl-guest-environment.test.ts index 0d14d9252b7..c249698e6f8 100644 --- a/src/main/wsl/wsl-guest-environment.test.ts +++ b/src/main/wsl/wsl-guest-environment.test.ts @@ -58,6 +58,33 @@ describe('probing', () => { await getWslGuestEnvironment('Debian') expect(runProcessMock).toHaveBeenCalledTimes(2) }) + + it('does not retain deadline timers after a concurrent probe settles', async () => { + vi.useFakeTimers() + try { + respondWithPayload(GOOD) + await Promise.all(Array.from({ length: 32 }, () => getWslGuestEnvironment('Ubuntu'))) + expect(vi.getTimerCount()).toBe(0) + expect(runProcessMock).toHaveBeenCalledTimes(1) + } finally { + vi.useRealTimers() + } + }) + + it('reads a warm cache without allocating deadline timers', async () => { + respondWithPayload(GOOD) + const environment = await getWslGuestEnvironment('Ubuntu') + const timeout = vi.spyOn(globalThis, 'setTimeout') + try { + for (let index = 0; index < 100; index++) { + expect(await getWslGuestEnvironment('Ubuntu')).toBe(environment) + } + expect(timeout).not.toHaveBeenCalled() + expect(runProcessMock).toHaveBeenCalledTimes(1) + } finally { + timeout.mockRestore() + } + }) }) describe('bad answers are not cached as good ones', () => { diff --git a/src/main/wsl/wsl-guest-environment.ts b/src/main/wsl/wsl-guest-environment.ts index 7f154314afb..4edfb862236 100644 --- a/src/main/wsl/wsl-guest-environment.ts +++ b/src/main/wsl/wsl-guest-environment.ts @@ -125,6 +125,10 @@ export function getWslGuestEnvironment( budgetMs = PROBE_TIMEOUT_MS ): Promise { const key = cacheKey(distro) + const cached = resolved.get(key) + if (cached) { + return Promise.resolve(cached) + } const retry = retryAfter.get(key) if (retry !== undefined && Date.now() >= retry) { inFlight.delete(key) @@ -154,13 +158,14 @@ export function getWslGuestEnvironment( // Why race: joining an in-flight probe used to mean waiting out the // *starter's* budget, so a joiner could reach its own command with 1ms -- // the exact hazard the budget plumbing was added to remove. + let timer: ReturnType return Promise.race([ existing, new Promise((resolve) => { - const timer = setTimeout(() => resolve(null), budgetMs) + timer = setTimeout(() => resolve(null), budgetMs) timer.unref?.() }) - ]) + ]).finally(() => clearTimeout(timer)) } // Store before awaiting so a burst collapses into one probe. // Why catch: runProcess REJECTS when the child cannot be started (ENOENT on a