mirror of
https://github.com/Pandipipas/scoreko-electron-dev.git
synced 2026-06-06 05:32:06 +00:00
Fix Electron shutdown by terminating NodeCG process tree (#13)
* Fix Electron shutdown by terminating NodeCG process tree * fix: adjust window dimensions for main and loading screens
This commit is contained in:
+77
-12
@@ -25,15 +25,17 @@ let mainWindow: BrowserWindow | null = null;
|
|||||||
let loadingWindow: BrowserWindow | null = null;
|
let loadingWindow: BrowserWindow | null = null;
|
||||||
let nodecgProcess: ChildProcess | null = null;
|
let nodecgProcess: ChildProcess | null = null;
|
||||||
let lastNodeCGOutput = "";
|
let lastNodeCGOutput = "";
|
||||||
|
let stopNodeCGPromise: Promise<void> | null = null;
|
||||||
|
let isQuitting = false;
|
||||||
|
|
||||||
function createMainWindow(): BrowserWindow {
|
function createMainWindow(): BrowserWindow {
|
||||||
const win = new BrowserWindow({
|
const win = new BrowserWindow({
|
||||||
show: false,
|
show: false,
|
||||||
title: APP_TITLE,
|
title: APP_TITLE,
|
||||||
width: 1440,
|
width: 1280,
|
||||||
height: 900,
|
height: 800,
|
||||||
minWidth: 960,
|
minWidth: 800,
|
||||||
minHeight: 640,
|
minHeight: 500,
|
||||||
backgroundColor: "#0f0f0f",
|
backgroundColor: "#0f0f0f",
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
contextIsolation: true,
|
contextIsolation: true,
|
||||||
@@ -73,8 +75,8 @@ function createLoadingWindow(): BrowserWindow {
|
|||||||
show: false,
|
show: false,
|
||||||
frame: false,
|
frame: false,
|
||||||
title: APP_TITLE,
|
title: APP_TITLE,
|
||||||
width: 420,
|
width: 300,
|
||||||
height: 280,
|
height: 300,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
movable: true,
|
movable: true,
|
||||||
minimizable: false,
|
minimizable: false,
|
||||||
@@ -180,6 +182,7 @@ function startNodeCG(): ChildProcess {
|
|||||||
...(USE_SYSTEM_NODE ? {} : { ELECTRON_RUN_AS_NODE: "1" }),
|
...(USE_SYSTEM_NODE ? {} : { ELECTRON_RUN_AS_NODE: "1" }),
|
||||||
},
|
},
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
|
detached: process.platform !== "win32",
|
||||||
shell: process.platform === "win32",
|
shell: process.platform === "win32",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -277,23 +280,76 @@ async function launch(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopNodeCG(): void {
|
function killNodeCGProcessTree(pid: number, signal: NodeJS.Signals): boolean {
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
const force = signal === "SIGKILL" ? "/F" : "";
|
||||||
|
const killer = spawn("taskkill", ["/pid", String(pid), "/T", ...(force ? [force] : [])], {
|
||||||
|
stdio: "ignore",
|
||||||
|
shell: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
killer.on("error", (error) => {
|
||||||
|
log(`taskkill error for pid=${pid}`, error);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
process.kill(-pid, signal);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
process.kill(pid, signal);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopNodeCG(): Promise<void> {
|
||||||
|
if (stopNodeCGPromise) {
|
||||||
|
return stopNodeCGPromise;
|
||||||
|
}
|
||||||
|
|
||||||
if (!nodecgProcess || nodecgProcess.killed) {
|
if (!nodecgProcess || nodecgProcess.killed) {
|
||||||
return;
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
const processToStop = nodecgProcess;
|
const processToStop = nodecgProcess;
|
||||||
const pid = processToStop.pid;
|
const pid = processToStop.pid;
|
||||||
|
|
||||||
|
if (typeof pid !== "number") {
|
||||||
|
log("NodeCG pid unavailable, skipping graceful stop");
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
log(`Stopping NodeCG pid=${pid}`);
|
log(`Stopping NodeCG pid=${pid}`);
|
||||||
processToStop.kill("SIGTERM");
|
killNodeCGProcessTree(pid, "SIGTERM");
|
||||||
|
|
||||||
|
stopNodeCGPromise = new Promise((resolve) => {
|
||||||
|
const complete = () => {
|
||||||
|
if (nodecgProcess === processToStop) {
|
||||||
|
nodecgProcess = null;
|
||||||
|
}
|
||||||
|
stopNodeCGPromise = null;
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
processToStop.once("exit", () => {
|
||||||
|
complete();
|
||||||
|
});
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (processToStop.exitCode === null && processToStop.signalCode === null) {
|
if (processToStop.exitCode === null && processToStop.signalCode === null) {
|
||||||
log(`NodeCG did not exit after SIGTERM, forcing SIGKILL pid=${pid}`);
|
log(`NodeCG did not exit after SIGTERM, forcing SIGKILL pid=${pid}`);
|
||||||
processToStop.kill("SIGKILL");
|
killNodeCGProcessTree(pid, "SIGKILL");
|
||||||
}
|
}
|
||||||
}, Math.max(0, NODECG_KILL_TIMEOUT_MS));
|
}, Math.max(0, NODECG_KILL_TIMEOUT_MS));
|
||||||
|
});
|
||||||
|
|
||||||
|
return stopNodeCGPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
function log(...args: unknown[]): void {
|
function log(...args: unknown[]): void {
|
||||||
@@ -331,8 +387,17 @@ app.on("window-all-closed", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on("before-quit", () => {
|
app.on("before-quit", (event) => {
|
||||||
stopNodeCG();
|
if (isQuitting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
isQuitting = true;
|
||||||
|
|
||||||
|
stopNodeCG().finally(() => {
|
||||||
|
app.quit();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on("will-quit", () => {
|
app.on("will-quit", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user