Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add process termination handling #23

Merged
merged 4 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 30 additions & 18 deletions electron/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const {
OperateCmd,
OperateDirectory,
} = require('./install');
const { killProcesses } = require('./processes');

// Attempt to acquire the single instance lock
const singleInstanceLock = app.requestSingleInstanceLock();
Expand Down Expand Up @@ -53,30 +54,22 @@ let tray,
operateDaemonPid,
nextAppProcess,
nextAppProcessPid;
let beforeQuitOnceCheck = false;

async function beforeQuit() {
if (beforeQuitOnceCheck) return;
beforeQuitOnceCheck = true;

await new Promise(function (resolve, reject) {
if (operateDaemonPid) {
try {
process.kill(operateDaemonPid, 'SIGKILL');
resolve(true);
} catch (error) {
resolve(false);
await killProcesses(operateDaemonPid);
} catch (e) {
console.error(e);
}
});
}

if (nextAppProcessPid) {
await new Promise(function (resolve, reject) {
try {
process.kill(nextAppProcessPid, 'SIGKILL');
resolve(true);
} catch (error) {
resolve(false);
}
});
try {
await killProcesses(nextAppProcessPid);
} catch (e) {
console.error(e);
}
}

tray && tray.destroy();
Expand Down Expand Up @@ -322,3 +315,22 @@ app.on('window-all-closed', () => {
app.on('before-quit', () => {
beforeQuit();
});

// PROCESS SPECIFIC EVENTS (HANDLES NON-GRACEFUL TERMINATION)

process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
// Clean up your child processes here
beforeQuit().then(() => {
process.exit(1); // Exit with a failure code
});
});

['SIGINT', 'SIGTERM'].forEach((signal) => {
process.on(signal, () => {
console.log(`Received ${signal}. Cleaning up...`);
beforeQuit().then(() => {
process.exit(0);
});
});
});
40 changes: 40 additions & 0 deletions electron/processes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const psTree = require('ps-tree');
const { exec } = require('child_process');

const unixKillCommand = 'kill -9';
const windowsKillCommand = 'taskkill /F /PID';

const isWindows = process.platform === 'win32';

function killProcesses(pid) {
return new Promise((resolve, reject) => {
psTree(pid, (err, children) => {
if (err) {
reject(err);
return;
}

// Array of PIDs to kill, starting with the children
const pidsToKill = children.map((p) => p.PID);
pidsToKill.push(pid); // Also kill the main process

const killCommand = isWindows ? windowsKillCommand : unixKillCommand;
const joinedCommand = pidsToKill
.map((pid) => `${killCommand} ${pid}`)
.join('; '); // Separate commands with a semicolon, so they run in sequence even if one fails. Also works on Windows.

exec(joinedCommand, (err) => {
if (
err?.message?.includes(isWindows ? 'not found' : 'No such process')
truemiller marked this conversation as resolved.
Show resolved Hide resolved
) {
return; // Ignore errors for processes that are already dead
}
reject(err);
});

resolve();
});
});
}

module.exports = { killProcesses };
Loading