Skip to content

Commit 21ca106

Browse files
7418claude
andcommitted
fix: graceful server shutdown with SIGKILL fallback
Add killServer() that sends SIGTERM via UtilityProcess.kill(), waits up to 3s, then escalates to SIGKILL via process.kill(pid). Fixes window-all-closed to await shutdown before quitting, and uses a persistent isQuitting flag in before-quit to prevent infinite loops. Closes #51 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7ce93b1 commit 21ca106

File tree

1 file changed

+43
-9
lines changed

1 file changed

+43
-9
lines changed

electron/main.ts

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,44 @@ let serverErrors: string[] = [];
1212
let serverExited = false;
1313
let serverExitCode: number | null = null;
1414
let userShellEnv: Record<string, string> = {};
15+
let isQuitting = false;
1516

1617
const isDev = !app.isPackaged;
1718

19+
/**
20+
* Gracefully shut down the server process.
21+
* Sends kill() (SIGTERM) first, waits up to 3s for exit,
22+
* then force-kills via process.kill(pid, SIGKILL) as fallback.
23+
*/
24+
function killServer(): Promise<void> {
25+
return new Promise((resolve) => {
26+
if (!serverProcess) {
27+
resolve();
28+
return;
29+
}
30+
31+
const pid = serverProcess.pid;
32+
33+
const timeout = setTimeout(() => {
34+
// Force kill via Node's process.kill with SIGKILL
35+
if (pid) {
36+
try { process.kill(pid, 'SIGKILL'); } catch { /* already dead */ }
37+
}
38+
serverProcess = null;
39+
resolve();
40+
}, 3000);
41+
42+
serverProcess.on('exit', () => {
43+
clearTimeout(timeout);
44+
serverProcess = null;
45+
resolve();
46+
});
47+
48+
// UtilityProcess.kill() sends SIGTERM
49+
serverProcess.kill();
50+
});
51+
}
52+
1853
/**
1954
* Verify that better_sqlite3.node in standalone resources is compatible
2055
* with this Electron runtime's ABI. If it was built for a different
@@ -343,11 +378,8 @@ app.whenReady().then(async () => {
343378
}
344379
});
345380

346-
app.on('window-all-closed', () => {
347-
if (serverProcess) {
348-
serverProcess.kill();
349-
serverProcess = null;
350-
}
381+
app.on('window-all-closed', async () => {
382+
await killServer();
351383
if (process.platform !== 'darwin') {
352384
app.quit();
353385
}
@@ -369,9 +401,11 @@ app.on('activate', async () => {
369401
}
370402
});
371403

372-
app.on('before-quit', () => {
373-
if (serverProcess) {
374-
serverProcess.kill();
375-
serverProcess = null;
404+
app.on('before-quit', async (e) => {
405+
if (serverProcess && !isQuitting) {
406+
isQuitting = true;
407+
e.preventDefault();
408+
await killServer();
409+
app.quit();
376410
}
377411
});

0 commit comments

Comments
 (0)