@mytec: before 3.0 REFACTOR
This commit is contained in:
140
desktop/main.js
140
desktop/main.js
@@ -269,17 +269,23 @@ function createMainWindow() {
|
||||
});
|
||||
|
||||
// Save window state on close and trigger shutdown
|
||||
mainWindow.on('close', () => {
|
||||
mainWindow.on('close', (event) => {
|
||||
log('[CLOSE] Window close event fired, isQuitting=' + isQuitting);
|
||||
try {
|
||||
const bounds = mainWindow.getBounds();
|
||||
store.set('windowState', bounds);
|
||||
} catch (_e) {}
|
||||
isQuitting = true;
|
||||
// Graceful shutdown is async but we also do sync kill as safety net
|
||||
gracefulShutdown().catch(() => {});
|
||||
killBackend();
|
||||
killAllBackendProcesses();
|
||||
|
||||
if (!isQuitting) {
|
||||
event.preventDefault();
|
||||
isQuitting = true;
|
||||
gracefulShutdown().then(() => {
|
||||
app.quit();
|
||||
}).catch(() => {
|
||||
killAllRfcpProcesses();
|
||||
app.quit();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Load frontend
|
||||
@@ -364,41 +370,86 @@ function killBackend() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Nuclear option: kill ALL rfcp-server processes by name.
|
||||
* This catches orphaned workers that PID-based kill misses.
|
||||
* Aggressive kill: multiple strategies to ensure ALL rfcp-server processes die.
|
||||
* Uses 4 strategies on Windows for maximum reliability.
|
||||
*/
|
||||
function killAllBackendProcesses() {
|
||||
log('[KILL] killAllBackendProcesses() — killing by process name...');
|
||||
function killAllRfcpProcesses() {
|
||||
log('[KILL] === Starting aggressive kill ===');
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
// Strategy 1: Kill by image name (most reliable)
|
||||
try {
|
||||
execSync('taskkill /F /IM rfcp-server.exe /T', {
|
||||
stdio: 'ignore',
|
||||
timeout: 5000
|
||||
log('[KILL] Strategy 1: taskkill /F /IM');
|
||||
execSync('taskkill /F /IM rfcp-server.exe', {
|
||||
stdio: 'pipe',
|
||||
timeout: 5000,
|
||||
windowsHide: true
|
||||
});
|
||||
log('[KILL] taskkill /IM rfcp-server.exe completed');
|
||||
log('[KILL] Strategy 1: SUCCESS');
|
||||
} catch (_e) {
|
||||
// Error means no processes found — OK
|
||||
log('[KILL] No rfcp-server.exe processes found (or already killed)');
|
||||
log('[KILL] Strategy 1: No processes or already killed');
|
||||
}
|
||||
|
||||
// Strategy 2: Kill by PID tree if we have PID
|
||||
if (backendPid) {
|
||||
try {
|
||||
log(`[KILL] Strategy 2: taskkill /F /T /PID ${backendPid}`);
|
||||
execSync(`taskkill /F /T /PID ${backendPid}`, {
|
||||
stdio: 'pipe',
|
||||
timeout: 5000,
|
||||
windowsHide: true
|
||||
});
|
||||
log('[KILL] Strategy 2: SUCCESS');
|
||||
} catch (_e) {
|
||||
log('[KILL] Strategy 2: PID not found');
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy 3: PowerShell kill (backup)
|
||||
try {
|
||||
log('[KILL] Strategy 3: PowerShell Stop-Process');
|
||||
execSync('powershell -Command "Get-Process rfcp-server -ErrorAction SilentlyContinue | Stop-Process -Force"', {
|
||||
stdio: 'pipe',
|
||||
timeout: 5000,
|
||||
windowsHide: true
|
||||
});
|
||||
log('[KILL] Strategy 3: SUCCESS');
|
||||
} catch (_e) {
|
||||
log('[KILL] Strategy 3: PowerShell failed or no processes');
|
||||
}
|
||||
|
||||
// Strategy 4: PowerShell CimInstance terminate (modern replacement for wmic)
|
||||
try {
|
||||
log('[KILL] Strategy 4: PowerShell CimInstance Terminate');
|
||||
execSync('powershell -NoProfile -Command "Get-CimInstance Win32_Process -Filter \\"name=\'rfcp-server.exe\'\\" | Invoke-CimMethod -MethodName Terminate"', {
|
||||
stdio: 'pipe',
|
||||
timeout: 5000,
|
||||
windowsHide: true
|
||||
});
|
||||
log('[KILL] Strategy 4: SUCCESS');
|
||||
} catch (_e) {
|
||||
log('[KILL] Strategy 4: No processes or failed');
|
||||
}
|
||||
} else {
|
||||
// Unix: pkill
|
||||
try {
|
||||
execSync('pkill -9 -f rfcp-server', {
|
||||
stdio: 'ignore',
|
||||
timeout: 5000
|
||||
});
|
||||
execSync('pkill -9 -f rfcp-server', { stdio: 'pipe', timeout: 5000 });
|
||||
log('[KILL] pkill rfcp-server completed');
|
||||
} catch (_e) {
|
||||
log('[KILL] No rfcp-server processes found');
|
||||
}
|
||||
}
|
||||
|
||||
backendPid = null;
|
||||
backendProcess = null;
|
||||
log('[KILL] === Kill sequence complete ===');
|
||||
}
|
||||
|
||||
/**
|
||||
* Graceful shutdown: ask backend to clean up, then force kill everything.
|
||||
* Graceful shutdown: API call first, then multi-strategy force kill.
|
||||
*/
|
||||
async function gracefulShutdown() {
|
||||
log('[SHUTDOWN] Requesting graceful shutdown...');
|
||||
log('[SHUTDOWN] Starting graceful shutdown...');
|
||||
|
||||
// Step 1: Ask backend to clean up workers and exit
|
||||
try {
|
||||
@@ -410,18 +461,18 @@ async function gracefulShutdown() {
|
||||
});
|
||||
clearTimeout(timeout);
|
||||
log('[SHUTDOWN] Backend acknowledged shutdown');
|
||||
// Wait for backend to cleanup
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
} catch (_e) {
|
||||
log('[SHUTDOWN] Backend did not respond — force killing');
|
||||
}
|
||||
|
||||
// Step 2: Wait briefly for graceful exit
|
||||
// Step 2: Force kill everything
|
||||
killAllRfcpProcesses();
|
||||
|
||||
// Step 3: Wait and verify
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
|
||||
// Step 3: PID-based kill (catches the main process)
|
||||
killBackend();
|
||||
|
||||
// Step 4: Name-based kill (catches orphaned workers)
|
||||
killAllBackendProcesses();
|
||||
log('[SHUTDOWN] Shutdown complete');
|
||||
}
|
||||
|
||||
// ── App lifecycle ──────────────────────────────────────────────────
|
||||
@@ -456,8 +507,7 @@ app.whenReady().then(async () => {
|
||||
app.on('window-all-closed', () => {
|
||||
log('[CLOSE] window-all-closed fired');
|
||||
isQuitting = true;
|
||||
killBackend();
|
||||
killAllBackendProcesses();
|
||||
killAllRfcpProcesses();
|
||||
|
||||
if (process.platform !== 'darwin') {
|
||||
app.quit();
|
||||
@@ -470,17 +520,23 @@ app.on('activate', () => {
|
||||
}
|
||||
});
|
||||
|
||||
app.on('before-quit', () => {
|
||||
log('[CLOSE] before-quit fired');
|
||||
isQuitting = true;
|
||||
killBackend();
|
||||
killAllBackendProcesses();
|
||||
app.on('before-quit', (event) => {
|
||||
log('[CLOSE] before-quit fired, isQuitting=' + isQuitting);
|
||||
if (!isQuitting) {
|
||||
event.preventDefault();
|
||||
isQuitting = true;
|
||||
gracefulShutdown().then(() => {
|
||||
app.quit();
|
||||
}).catch(() => {
|
||||
killAllRfcpProcesses();
|
||||
app.quit();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.on('will-quit', () => {
|
||||
log('[CLOSE] will-quit fired');
|
||||
killBackend();
|
||||
killAllBackendProcesses();
|
||||
killAllRfcpProcesses();
|
||||
|
||||
if (backendLogStream) {
|
||||
try { backendLogStream.end(); } catch (_e) {}
|
||||
@@ -508,21 +564,19 @@ process.on('exit', () => {
|
||||
}
|
||||
|
||||
// Name-based kill — catches orphaned workers
|
||||
killAllBackendProcesses();
|
||||
killAllRfcpProcesses();
|
||||
});
|
||||
|
||||
// Handle SIGINT/SIGTERM (Ctrl+C, system shutdown)
|
||||
process.on('SIGINT', () => {
|
||||
try { log('[SIGNAL] SIGINT received'); } catch (_e) {}
|
||||
killBackend();
|
||||
killAllBackendProcesses();
|
||||
killAllRfcpProcesses();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
try { log('[SIGNAL] SIGTERM received'); } catch (_e) {}
|
||||
killBackend();
|
||||
killAllBackendProcesses();
|
||||
killAllRfcpProcesses();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user