Skip to content

Commit 1ed2d70

Browse files
committed
fix(shells): ref the kill-grace timer so Windows shutdown drain can finish
The second windows-latest hang after the OAuth queue fix had the same shape: shutdown-drain's tests passed and the isolate process never exited. The kill-grace wait inside waitForBackgroundShellClose was an unref'd timer, and Bun on Windows stops servicing unref'd timers when a pending promise is the only remaining work — so drainAndShutdown waited on a resolution that never came. The grace timer is now deliberately ref'd: it self-clears within its two-second bound (or earlier on close), so it cannot keep a healthy process alive, while the long-lived idle and absolute deadline timers stay unref'd as before. The test fixture timer gets the same treatment.
1 parent 1ace71a commit 1ed2d70

2 files changed

Lines changed: 13 additions & 4 deletions

File tree

src/adapters/cursor/native-exec-shell.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,12 @@ function waitForBackgroundShellClose(entry: BackgroundShellEntry): Promise<boole
340340
settled = true;
341341
resolveWait(false);
342342
}, CURSOR_BACKGROUND_SHELL_TERM_GRACE_MS);
343-
unrefTimer(timer);
343+
// Deliberately REF'D: this is the bounded kill-grace wait that shutdown
344+
// drain awaits. Bun on Windows can starve unref'd timers when a pending
345+
// promise is the only other work, which would leave drainAndShutdown
346+
// waiting on this resolution forever. The timer self-clears within the
347+
// 2-second grace window (or earlier on close), so a ref cannot keep the
348+
// process alive beyond that bound.
344349
void entry.closePromise.then(() => {
345350
if (settled) return;
346351
settled = true;

tests/shutdown-drain.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,13 @@ describe("background shell shutdown drain", () => {
119119
const unresolvedChild = installShutdownShell();
120120
setBackgroundShellRuntimeForTests({
121121
setTimer(callback) {
122-
const timer = setTimeout(callback, 0);
123-
timer.unref?.();
124-
return timer;
122+
// Keep this fixture timer REF'D: Bun on Windows can stop servicing
123+
// unref'd timers while the test's only pending work is a promise,
124+
// which left drainAndShutdown waiting forever and hung the isolate
125+
// process until the 20-minute CI job timeout (same starvation the
126+
// OAuth queue tests hit). A ref'd 0ms timer fires immediately and
127+
// cannot keep the process alive.
128+
return setTimeout(callback, 0);
125129
},
126130
});
127131
const unresolvedServer = fakeServer();

0 commit comments

Comments
 (0)