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>
This commit is contained in:
7418andClaude Opus 4.6 committed 2026-02-10 22:55:04 +08:00
1 parent 7ce93b1102
commit 21ca106720
1 file changed
+43 -9
+43 -9
View File
@@ -12,9 +12,44 @@ let serverErrors: string[] = [];
let serverExited = false;
let serverExitCode: number | null = null;
let userShellEnv: Record<string, string> = {};
let isQuitting = false;
const isDev = !app.isPackaged;
/**
* Gracefully shut down the server process.
* Sends kill() (SIGTERM) first, waits up to 3s for exit,
* then force-kills via process.kill(pid, SIGKILL) as fallback.
*/
function killServer(): Promise<void> {
return new Promise((resolve) => {
if (!serverProcess) {
resolve();
return;
}
const pid = serverProcess.pid;
const timeout = setTimeout(() => {
// Force kill via Node's process.kill with SIGKILL
if (pid) {
try { process.kill(pid, 'SIGKILL'); } catch { /* already dead */ }
}
serverProcess = null;
resolve();
}, 3000);
serverProcess.on('exit', () => {
clearTimeout(timeout);
serverProcess = null;
resolve();
});
// UtilityProcess.kill() sends SIGTERM
serverProcess.kill();
});
}
/**
* Verify that better_sqlite3.node in standalone resources is compatible
* with this Electron runtime's ABI. If it was built for a different
@@ -343,11 +378,8 @@ app.whenReady().then(async () => {
}
});
app.on('window-all-closed', () => {
if (serverProcess) {
serverProcess.kill();
serverProcess = null;
}
app.on('window-all-closed', async () => {
await killServer();
if (process.platform !== 'darwin') {
app.quit();
}
@@ -369,9 +401,11 @@ app.on('activate', async () => {
}
});
app.on('before-quit', () => {
if (serverProcess) {
serverProcess.kill();
serverProcess = null;
app.on('before-quit', async (e) => {
if (serverProcess && !isQuitting) {
isQuitting = true;
e.preventDefault();
await killServer();
app.quit();
}
});