Skip to content

Quitting the app leaves dev servers and watchers running #83

Closed

Description

@juanmaguitar

What happens

Quitting the app while a dev server or a watch task is running leaves those processes alive. The Playground server keeps its port and the watcher keeps rebuilding until they are killed by hand — at a Contributor Day that means Task Manager / Activity Monitor. On the next launch, the web-server "already reachable" probe (src/main.js:876-888) can silently adopt the previous session's orphaned server.

Root cause

Two independent gaps:

  1. There is no quit hook anywhere in src/main.js — only the window-all-closed handler (src/main.js:392-394). Nothing sweeps the tracked children (runningInstalls, runningScripts, playgroundServers, the web server) on exit.
  2. Even when the app does kill a child (src/main.js:736, 848, 995), the signal reaches only the direct child. The processes that matter are grandchildren (script runner → shell → grunt → node); on Windows especially, the tree survives.

Related leak: the Windows shim directory created per run in the temp folder (src/main.js:45) is never removed — one leftover directory per launch.

Expected

Quitting the app ends everything it started, on all platforms.

Activity

  1. added 2 commits that reference this issue on Jul 31, 2026
    c8d2d06
    a7a607e
  2. juanmaguitar commented on Jul 31, 2026

    @juanmaguitar
    CollaboratorAuthor

    PR #98 lands the sweep (before-quit + taskkill /T on Windows, detached spawn + process-group signal on POSIX) but keeps a known residual gap on Windows: taskkill /T walks parent links at kill time, so a grandchild whose intermediate parent already died can survive. Observed on a Windows VM as a grunt _watch process (spawned via the node shim, so it shows in Task Manager as "WordPress Contributor Toolkit") outliving the quit with its ParentProcessId pointing at a dead process.

    Possible directions for the complete fix, in rough order of appeal: have the runners own their children's lifetime (each runner kills its subtree when its stdin closes, i.e. when the app dies — no quit-time race at all); or enumerate the descendant closure from a Win32_Process snapshot taken before any kill; or Windows job objects via a native module. Not release-blocking: the leak is bounded (one watcher process) and strictly smaller than trunk's leak-everything behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions