fix(wsl): start no running-distro read inside the stop settle window (#34372)

* fix(wsl): start no running-distro read inside the stop settle window

settledStartedAfter slept with one setTimeout until the settle anchor. Node truncates timer
delays to whole ms against a ms loop clock, so the wake can land up to ~2ms before the anchor
by performance.now(), and the probe then started inside the window and could still see the
stopping distro as running. That is the hook relay stop-settle flake ("expected true to be
false"). Sleep until performance.now() has passed the anchor.

* test(wsl): spy on setTimeout without a type assertion

* test(wsl): fire the spied timer far early so a single sleep always fails
This commit is contained in:
Neil
2026-10-27 03:29:12 -07:00
committed by GitHub
parent 6a7d9aad62
commit 71f0c74d9e
2 changed files with 30 additions and 4 deletions
+26 -1
View File
@@ -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'])
+4 -3
View File
@@ -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
}