Skip to content

Windows: hard exit after shutdown, a hung renderer ends only its tile, pipe_probe serves its own pages - #58

Merged
wenkaifan0720 merged 1 commit into
mainfrom
fix/windows-parity-gaps
Sep 24, 2026
Merged

wenkaifan0720 merged 1 commit into
mainfrom
fix/windows-parity-gaps

Conversation

@wenkaifan0720

@wenkaifan0720 wenkaifan0720 commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Closes the three Windows parity gaps left after #57.

1. Hard exit after shutdown (as macOS ArmHardExit / ExtendHardExitForTeardown)

A shutdown request (kOpShutdown, the pipe closing, the crash-loop host exit) arms a watchdog in cef_host that ends the process if it is still running 6 s later, or 30 s after its message loop has quit (a stuck CefShutdown). It logs [cef_host] still running after shutdown (<why>); exiting now to stderr and exits with code 0, the same as macOS.

Two Windows-specific choices:

  • It exits with TerminateProcess, not exit(). exit() runs DLL detach code, which can block on a lock the wedged thread holds.
  • The watchdog thread starts with the process, and arming it only sets the deadline. Starting a thread takes the loader lock, which a wedged thread may be holding.

The timings and the wait loop (it re-reads the deadline after every sleep, so the teardown extension is kept) are in cef_host_policy.h, covered by policy_test.cc.

2. A hung renderer ends only its tile (as macOS since #49)

  • Plugin: an unanswered liveness ping now calls ReportBrowserGone(session, "crashed"), the same path as kOpCreateFailed and kOpBrowserGone. That one session gets processGone('crashed') and is disposed; the host and its other tiles carry on. Before, the plugin killed the host.
  • Host: the ping is now put to the renderer as a process message and answered from the renderer's main thread, as on macOS, instead of being evaluated in the page. So a page that breaks window.cefQuery or JSON isn't taken for hung, and a page's own eval: reply under the ping's id is refused.
  • Escalation to the host: macOS's sweep ends a host whose GPU process was replaced, because its views never paint again. I tested this on Windows: after chrome://gpucrash the tiles keep presenting at about 60 fps, and new tiles on the host paint. So Windows doesn't watch for it, and the smoke test now checks that the tiles survive. Nothing else escalates a hang to the host, same as macOS. When the hung tile is the host's last, disposing it shuts the host down, as any dispose does.

3. pipe_probe serves its own pages

pipe_probe runs a small HTTP server on 127.0.0.1 for its two pages, instead of example.com and iana.org. A slow CI network once held the host in CefShutdown for over 20 s. It now also:

  • prints each host's stdout and stderr (read from a pipe);
  • checks that a clean shutdown doesn't end with the watchdog's line;
  • runs a second host whose UI thread it suspends before kOpShutdown. That host has to exit on its own about 6 s later, with code 0 and the watchdog's line. On CI it exits 6.03 s after kOpShutdown.

Smoke test

The smoke test gains two cases, and each runs in both compositing modes:

  • Hung renderer: on a shared host, one tile's page loops forever.
    • Only that tile gets processGone('crashed'), about 32 s after the hang.
    • A page beside it that breaks evals is left alone.
    • The animated tile keeps painting and answering.
  • Replaced GPU process: chrome://gpucrash replaces the host's GPU process (checked by pid).
    • The tiles keep painting.
    • A new tile on the host paints.
    • No processGone.

The README ("What works" notes), PROTOCOL.md, both CHANGELOGs (under a new "## Unreleased") and the plugin comments are updated. I haven't merged it. The windows-build pass count is in a comment below.

🤖 Generated with Claude Code

…, pipe_probe serves its own pages

cef_host:
- A shutdown request (kOpShutdown, the pipe closing, a crash-loop host
  exit) arms a watchdog that ends the process 6 s later if the message
  loop hasn't quit, and 30 s after it has (a stuck CefShutdown), with the
  macOS log line. Its thread starts with the process, since starting one
  takes the loader lock a wedged thread may hold.
- The liveness ping is answered by the renderer's main thread (a process
  message), not the page; a page can't answer under the ping's id.

Plugin:
- A renderer that leaves the liveness ping unanswered ends only its tile
  (processGone crashed), not the host, as on macOS.
- A replaced GPU process isn't watched: unlike macOS, Windows tiles keep
  painting after Chromium relaunches it (checked in CI).

Tests:
- pipe_probe serves its pages on 127.0.0.1, prints each host's output,
  and checks that a host whose UI thread is suspended at kOpShutdown exits
  on its own about 6 s later with the watchdog's line.
- The smoke test hangs one tile's renderer on a shared host (only it goes,
  and a page that breaks evals beside it stays) and replaces a host's GPU
  process with chrome://gpucrash (its tiles keep painting).
- policy_test covers the watchdog's wait.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@wenkaifan0720
wenkaifan0720 force-pushed the fix/windows-parity-gaps branch from 5e6521d to 67b1958 Compare September 24, 2026 14:12
@wenkaifan0720
wenkaifan0720 marked this pull request as ready for review September 24, 2026 14:12
@wenkaifan0720

Copy link
Copy Markdown
Collaborator Author

windows-build on 67b1958 (run 36011088679): 6 of 6 attempts green (the first run plus 5 reruns).

attempt job windows-build pipe_probe (both modes) wedged host exits after kOpShutdown hung tile gone after the hang GPU replaced, tiles paint crash-loop rounds FAIL lines
1 107671673958 success 2/2 6.03 s, 6.05 s 31.8 s, 31.6 s 2/2 6/6 0
2 107676367127 success 2/2 6.03 s, 6.03 s 31.4 s, 30.7 s 2/2 6/6 0
3 107680610837 success 2/2 6.03 s, 6.03 s 30.6 s, 30.0 s 2/2 6/6 0
4 107685171543 success 2/2 6.03 s, 6.05 s 30.3 s, 31.2 s 2/2 6/6 0
5 107689676615 success 2/2 6.03 s, 6.03 s 30.9 s, 31.0 s 2/2 6/6 0
6 107693980571 success 2/2 6.05 s, 6.05 s 31.0 s, 30.2 s 2/2 6/6 0

That is 12 of 12 pipe_probe runs, 12 of 12 hung-renderer cases (the tile beside it that breaks evals was never taken for hung), 12 of 12 GPU-replacement cases, and 36 of 36 crash-loop rounds. On every run, the plugin log's only liveness line was the one for the hung tile: browser 2's renderer left the liveness ping unanswered for 15s — reporting it gone. analyze-test and macos-build passed on every attempt.

🤖 Generated with Claude Code

@wenkaifan0720
wenkaifan0720 merged commit f04280a into main Sep 24, 2026
18 checks passed
@wenkaifan0720
wenkaifan0720 deleted the fix/windows-parity-gaps branch September 24, 2026 15:20
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.

1 participant