Skip to content

Add backend orphan process cleanup on startup#51

Open
wpflueger wants to merge 1 commit intomainfrom
fix/backend-orphan-cleanup
Open

Add backend orphan process cleanup on startup#51
wpflueger wants to merge 1 commit intomainfrom
fix/backend-orphan-cleanup

Conversation

@wpflueger
Copy link
Owner

Summary

  • Writes a backend.pid file when the Python backend starts, removes it on normal exit
  • On app launch, checks for a stale PID file from a previous non-graceful exit and kills the orphaned process
  • Prevents GPU-consuming Python processes from running indefinitely after Electron crashes or is force-killed

Test plan

  • Normal startup/shutdown: verify backend.pid is created and removed
  • Force-kill Electron (Task Manager), relaunch, and verify the orphaned backend is cleaned up
  • Verify no errors when PID file doesn't exist or references a dead process
  • Verify normal backend operation is unaffected

Closes #38

🤖 Generated with Claude Code

If Electron crashes or is force-killed, the Python backend process is
left orphaned consuming GPU resources. This adds:
- PID file tracking: write backend PID on start, remove on exit
- Orphan cleanup: on next launch, check for stale PID file and kill
  the orphaned process before starting a new one

Closes #38

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 9, 2026 03:43
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a PID-file based mechanism to clean up orphaned Python backend processes on Electron startup to prevent GPU-consuming backends from surviving non-graceful app exits (issue #38).

Changes:

  • Add PID file creation/removal in startBackend() and a cleanupOrphanedBackend() helper that kills a previously-recorded PID.
  • Invoke orphan cleanup at the start of the backend startup sequence in the Electron main process.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
apps/desktop/electron/main.ts Calls orphan-backend cleanup before starting backend dependencies and process.
apps/desktop/electron/backend.ts Implements PID file persistence and orphan process termination logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +293 to +294
cleanupOrphanedBackend();

Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling cleanupOrphanedBackend() unconditionally on startup can break multi-instance behavior: if the user launches a second instance while the first is still running, the PID file from the first instance will exist and the second instance will kill the first instance’s backend. If the app is intended to be single-instance, add app.requestSingleInstanceLock() early and exit the secondary instance; otherwise, include a parent-PID / instance identifier in the PID file and only clean up when the owning Electron process is no longer running.

Copilot uses AI. Check for mistakes.
Comment on lines +114 to +119
// Track the PID so orphaned processes can be cleaned up on next launch
if (child.pid != null) {
writePidFile(child.pid);
}
child.on("exit", () => removePidFile());

Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New PID file behavior (write on spawn, remove on backend exit, and orphan cleanup) isn’t covered by the existing backend.test.ts suite. Adding unit tests here would help prevent regressions (mock electron.app.getPath, fs reads/writes, and process.kill) and ensure the PID file is written/removed and cleanup only kills when appropriate.

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +40
const pid = parseInt(fs.readFileSync(pidFile, "utf-8").trim(), 10);
if (!Number.isNaN(pid)) {
// Check if process is still alive
process.kill(pid, 0);
// If we get here, process exists — kill it
console.log(`Killing orphaned backend process (PID ${pid})`);
process.kill(pid, "SIGTERM");
}
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PID file stores only a numeric PID, and cleanupOrphanedBackend() will SIGTERM that PID if it exists. If the app is relaunched long after a crash, the OS may have reused that PID for an unrelated process, which could cause this code to terminate the wrong process. Consider storing additional identifying info (e.g., the previous Electron parent PID and verifying it is no longer alive, and/or validating the process command line/executable where possible) before issuing a kill, and only kill when you can confidently confirm it’s the backend you started.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +45
} catch {
// Process doesn't exist or we can't kill it — either way, clean up
}
removePidFile();
};
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The blanket catch {} combined with unconditional removePidFile() means the PID file is deleted even when the backend process still exists but cannot be terminated (e.g., process.kill(pid, 0) / SIGTERM throws EPERM). That can leave a GPU-consuming orphan running while preventing subsequent launches from retrying cleanup. Consider inspecting the error code and only deleting the PID file when the process is confirmed gone (ESRCH) or after a successful termination (and log/report failures).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Backend process not cleaned up on non-graceful Electron exit

2 participants