diff --git a/src/main/wsl-background-distro-gate.test.ts b/src/main/wsl-background-distro-gate.test.ts index 3da5b338c21..75b226a9034 100644 --- a/src/main/wsl-background-distro-gate.test.ts +++ b/src/main/wsl-background-distro-gate.test.ts @@ -12,7 +12,9 @@ import { deferUntilWslDistroRunning, isWslDistroRunning, isWslDistroRunningForBackgroundWork, - noteWslDistrosRunning + noteWslDistroMayHaveStopped, + noteWslDistrosRunning, + WSL_STOP_SETTLE_MS } from './wsl-background-distro-gate' import { _resetRunningWslDistroCacheForTests, @@ -57,6 +59,29 @@ describe('isWslDistroRunning', () => { }) }) +describe('the stop settle window', () => { + it('starts no running read inside the window, even when a timer fires early', async () => { + // Node timers can wake ~2ms short; firing far earlier makes a single sleep fail every time. + const realSetTimeout = globalThis.setTimeout + vi.spyOn(globalThis, 'setTimeout').mockImplementation((handler, ms, ...args) => + realSetTimeout(handler, Math.max(0, (ms ?? 0) - 100), ...args) + ) + const probeStarts: number[] = [] + wslMocks.listRunningWslDistrosAsync.mockImplementation((options) => + resolveRunningWslDistros(async () => { + probeStarts.push(performance.now()) + return [] + }, options) + ) + const settledAt = performance.now() + WSL_STOP_SETTLE_MS + noteWslDistroMayHaveStopped('Ubuntu') + + await expect(isWslDistroRunning('Ubuntu')).resolves.toBe(false) + expect(probeStarts).toHaveLength(1) + expect(probeStarts[0]).toBeGreaterThanOrEqual(settledAt) + }) +}) + describe('resolveRunningWslDistros startedAfter', () => { it('answers from a confirmed read that started after the anchor, and reads again for a later one', async () => { const probe = vi.fn(async () => ['Ubuntu']) diff --git a/src/main/wsl-background-distro-gate.ts b/src/main/wsl-background-distro-gate.ts index be3dc9b70bd..457f9b6cf3e 100644 --- a/src/main/wsl-background-distro-gate.ts +++ b/src/main/wsl-background-distro-gate.ts @@ -83,9 +83,10 @@ async function settledStartedAfter( woken ?? -Infinity ) const anchor = floor === -Infinity ? startedAfter : Math.max(startedAfter ?? -Infinity, floor) - const waitMs = (anchor ?? -Infinity) - performance.now() - if (waitMs > 0) { - await new Promise((resolve) => setTimeout(resolve, waitMs)) + // Timers truncate to whole ms on a ms clock and can wake ~2ms short; sleep until the anchor has passed. + const settled = anchor ?? -Infinity + while (performance.now() < settled) { + await new Promise((resolve) => setTimeout(resolve, Math.ceil(settled - performance.now()))) } return anchor }