@mytec: before 3.0 REFACTOR

This commit is contained in:
2026-02-01 14:26:17 +02:00
parent acc90fe538
commit 1dde56705a
13 changed files with 1759 additions and 61 deletions

View File

@@ -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);
});