Skip to content

fix(dev): forward termination signals to Electron child (#899) - #900

Open
iddogino wants to merge 1 commit into
alex8088:masterfrom
iddogino:fix/forward-termination-signals
Open

fix(dev): forward termination signals to Electron child (#899)#900
iddogino wants to merge 1 commit into
alex8088:masterfrom
iddogino:fix/forward-termination-signals

Conversation

@iddogino

Copy link
Copy Markdown

Summary

Fixes #899. In startElectron, forward SIGINT/SIGTERM/SIGHUP from the electron-vite parent to the spawned Electron child, matching Electron's own CLI wrapper (node_modules/electron/cli.js).

Why

electron-vite spawns the Electron binary directly via spawn(electronPath, ...) and does not trap signals. When the user hits Ctrl-C in a dev session:

  • SIGINT is delivered to the foreground process group once.
  • The Node parent has no SIGINT handler, so it exits immediately with Node's default behavior.
  • The Electron child receives only that single SIGINT.

Under a direct ./node_modules/.bin/electron . launch the picture is different: the Electron CLI shim installs a handler that forwards SIGINT to its child, so the Electron child receives SIGINT twice (once from the pgroup, once forwarded). On macOS, an Electron app whose before-quit handler calls event.preventDefault() and then later calls app.quit() from an async continuation can stall in AppKit's event loop — never reaching [NSApp terminate:]. The second SIGINT forwarded by the shim is what knocks Electron out of that stuck state.

By matching that forwarding behavior in electron-vite, electron-vite dev now exits as cleanly as electron . does for that app.

A reduced test was run against the repro in the issue (https://github.com/iddogino/electron-vite-quit-repro):

scenario result
unpatched, Ctrl-C via pty hangs — 3 Electron processes lingering
parent kept alive but no forwarding still hangs
parent kept alive and signal forwarded (this PR) clean exit, 0 lingering

Notes on the implementation

  • currentElectronPs tracks the latest-spawned child so that when the watch-on-rebuild path (server.ts) kills the old Electron and spawns a new one, signals go to the current child, not a stale reference.
  • signalHandlersInstalled guards against accumulating process.on handlers across dev restarts.
  • The ps.on('close', process.exit) behavior is unchanged, so a normal in-app quit still exits the parent as before.

Test plan

  • pnpm run lint, pnpm run typecheck, pnpm run build all pass.
  • prettier --check src/electron.ts passes.
  • Built dist dropped into the repro — pty Ctrl-C exits cleanly (0 lingering Electron processes).
  • Manual sanity test on the repro in a real terminal with Ctrl-C.
  • Manual sanity test of the watch-restart path (edit src/main/index.js, confirm restart still works).

Spawn the Electron binary directly in dev/preview mode but forward
SIGINT/SIGTERM/SIGHUP from the electron-vite parent to the child,
matching Electron's own CLI wrapper (node_modules/electron/cli.js).

Without this, Ctrl-C sends SIGINT to the foreground process group once:
the Node parent exits immediately (no handler) and the Electron child
gets only a single SIGINT. On macOS, an Electron app whose before-quit
handler calls preventDefault() and later calls app.quit() can stall in
AppKit's event loop in that path. Under a direct 'electron .' launch
the CLI shim forwards SIGINT as well, so Electron gets it twice and
exits cleanly. Matching that behavior here fixes the hang.

Fixes alex8088#899
@alex8088

Copy link
Copy Markdown
Owner

Thanks for the PR. I will perform some local tests to verify the changes.

@callummr

Copy link
Copy Markdown

Have you had a chance to review this @alex8088 ? I have a use-case that will benefit from it as my main launches a few subprocesses and doesn't know to kill them without this patch. Cheers!

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.

[macOS] app.quit() after preventDefault'd before-quit hangs under electron-vite dev (but not with direct electron launch)

3 participants