Skip to content

More safety checks around IPC communication - #849

Merged
fredrikekelund merged 3 commits into
trunkfrom
f26d/more-is-destroyed-checks
Jan 28, 2025
Merged

More safety checks around IPC communication#849
fredrikekelund merged 3 commits into
trunkfrom
f26d/more-is-destroyed-checks

Conversation

@fredrikekelund

Copy link
Copy Markdown
Contributor

Related issues

Proposed Changes

With the help of some AI debugging in Sentry, I determined that https://github.com/Automattic/dotcom-forge/issues/10346 seems to be happening because we send IPC messages to windows that have been destroyed. This PR adds some additional isDestroyed checks to mitigate that (see 0ae3042).

I also took the opportunity to refactor withMainWindow into a function that returns a promise instead. There's really no reason for it to accept a callback, so this helps keep our code clean and simple.

Testing Instructions

Smoke test the app. Ensure that the About menu, the settings, and the fullscreen view work as expected.

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?
@fredrikekelund
fredrikekelund requested a review from a team January 27, 2025 14:42
@fredrikekelund fredrikekelund self-assigned this Jan 27, 2025

@wojtekn wojtekn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The code change looks clear, and it's more readable now.

I smoke tested different Studio features and haven't noticed any issues.

@fredrikekelund
fredrikekelund merged commit e7a67f7 into trunk Jan 28, 2025
@fredrikekelund
fredrikekelund deleted the f26d/more-is-destroyed-checks branch January 28, 2025 09:14
bgrgicak pushed a commit that referenced this pull request Jan 29, 2025
* WIP

* withMainWindow -> getMainWindow

* Refinements
fredrikekelund pushed a commit that referenced this pull request May 29, 2026
## Related issues

- Fixes
[STU-1750](https://linear.app/a8c/issue/STU-1750/studio-reopens-multiple-times-when-quitting-during-site-startup)

## How AI was used in this PR

Claude Code investigated the bug, identified the root cause
(create-if-missing branch in `getMainWindow()` racing with async IPC
fan-out during the `will-quit` shutdown window), proposed the minimal
gate at `sendIpcEventToRenderer`, and applied the change. I reviewed the
diff and confirmed both the fix and its placement before committing.

## Proposed Changes

- Add an `isAppQuitting` flag in `apps/studio/src/ipc-utils.ts` with an
exported `markAppQuitting()` setter. While set, `sendIpcEventToRenderer`
returns early without calling `getMainWindow()`.
- Call `markAppQuitting()` at the start of the `will-quit` handler in
`apps/studio/src/index.ts`, before any other cleanup.

### Why this works

When the user confirms "Stop sites" on quit, `will-quit` calls
`event.preventDefault()` and waits up to 6 s for `stopAllServers()` to
drain. During that window, in-flight async callbacks (site-startup
events, snapshot events, sync progress, etc.) keep firing through
`sendIpcEventToRenderer()`. Each call routes through `getMainWindow()` —
and because the original window has typically already been closed by
Electron's shutdown sequence, `getMainWindow()` falls through to
`createMainWindow()` and spawns a fresh `BrowserWindow`. One pending
event = one ghost window. With ~6 sites starting simultaneously on a
Linux VM, you reliably see ~6 windows reopen.

Blocking the fan-out at the `sendIpcEventToRenderer` chokepoint is the
smallest fix: it catches every async-IPC path without changing
`getMainWindow`'s signature or touching its 15+ other call sites (menus,
deeplinks, update dialog, etc.), which are user-triggered and can't fire
post-`will-quit` anyway.

`will-quit` (not `before-quit`) is the right gate point: `before-quit`
can still be cancelled by the sync-in-progress dialog or the "Cancel"
button on the running-sites dialog. Once `will-quit` fires, the app is
committed.

### Regression origin

The mechanic was introduced in #86 (May 2024) for OAuth deep-link
handling. It became today's user-visible bug in #849 (Jan 2025), which
routed `sendIpcEventToRenderer` through `getMainWindow()`. The bug got
more reproducible recently due to Linux VM testing (slower shutdown
widens the race window) plus PR #3350 removing an accidental escape
hatch that bypassed the quit dialog when the CLI wasn't installed.

## Testing Instructions

Reproduce on `trunk` first, then verify the fix on this branch.

**Repro on trunk (Linux/Windows easier; macOS rarer):**

1. Have several Studio sites created (~6 makes it obvious).
2. Quit Studio so all sites stop.
3. Re-launch Studio.
4. Start all sites at once.
5. **Before** they finish booting, quit Studio.
6. Observe: Studio reopens 4–6 times in quick succession.

**Verify on this branch:**

1. Repeat steps 1–5 above.
2. Studio should quit cleanly with no ghost windows.

## Pre-merge Checklist

- [x] Have you checked for TypeScript, React or other console errors?
- [x] Manual repro on Linux (with several sites)
- [x] Manual repro on Windows (with several sites)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants