Skip to content

fix: report renderer hangs independently of system sleep and window visibility - #53529

Open
codebytere wants to merge 7 commits into
mainfrom
fix/hang-monitor-power-resume
Open

fix: report renderer hangs independently of system sleep and window visibility#53529
codebytere wants to merge 7 commits into
mainfrom
fix/hang-monitor-power-resume

Conversation

@codebytere

Copy link
Copy Markdown
Member

Description of Change

The unresponsive / responsive events on webContents now reflect the renderer, not system sleep or window visibility. Two cases produced events that had nothing to do with the renderer's state, and apps that reload or kill a renderer on unresponsive (or wait for responsive) acted on them:

  1. After the system wakes. The input event ack timeout behind unresponsive runs on base::TimeTicks, which keeps counting through sleep on Windows (QPC) but stands still on macOS and Linux. A timeout armed before the machine sleeps therefore fires the moment it wakes, while every process is still paging back in, and the renderer is reported unresponsive. Chromium's GPU watchdog already pauses across suspend for this reason; the renderer hang monitor has no power awareness. WebContents::RendererUnresponsive() now checks base::PowerMonitor: while the system is suspended, or within input::kHungRendererDelay of the last resume, it runs the hang-monitor restarter it is handed instead of emitting, so a renderer that is still stuck a full delay after the wake is reported as before.

  2. When a hung window is hidden. RenderWidgetHostImpl::WasHidden() stopped the timeout with StopInputEventAckTimeout(), which also declares the renderer responsive. Minimizing, occluding or screen-locking a hung window therefore emitted responsive with no recovery behind it and cleared the unresponsive state, so the real recovery later emitted nothing. WasHidden() now pauses the timeout without touching responsiveness (a PauseInputEventAckTimeout() split out of StopInputEventAckTimeout(), added to the existing refactor_unfilter_unresponsive_events.patch); WasShown() re-arms it for a widget that is still hung, and a real input ack still emits responsive, hidden or not. Chrome relies on the old behavior only to dismiss its hung-page dialog on tab backgrounding, which Electron does not build.

before after
input pending, system sleeps and wakes (Windows) unresponsive ~1 s after wake nothing unless still hung 15 s after wake
hung window is minimized / occluded responsive immediately; nothing when it actually recovers nothing on hide; unresponsive again when shown if still hung; responsive when it recovers

The testing-only binding gains simulatePowerEvent('suspend' | 'resume') (drives base::PowerMonitorSource::ProcessPowerEvent) so the spec can exercise the resume path.

Checklist

  • PR description included and stakeholders cc'd
  • npm test passes (new webContents module > unresponsive event cases: not emitted within a hang delay of a resume; no responsive on hide, unresponsive again on show, responsive on recovery; baseline hang still reported; each fails with its fix reverted)
  • Relevant documentation is changed or added (behavior fix; unresponsive/responsive docs unchanged)
  • PR release notes describe the change in a way relevant to app developers, and are capitalized, punctuated, and past tense.
  • I have reviewed and verified the changes

Release Notes

Notes: Fixed webContents emitting unresponsive right after the system wakes from sleep on Windows, and emitting responsive when a hung window was hidden rather than when it recovered.

The input event ack timeout that drives the `unresponsive` event runs on
base::TimeTicks, which keeps counting through system sleep on Windows
(QPC) but stands still on macOS and Linux. A timeout armed before the
machine sleeps therefore fires the moment it wakes, while every process
is still paging back in, and the renderer is reported unresponsive
(RenderWidgetHostImpl::OnInputEventAckTimeout -> RendererUnresponsive);
apps commonly answer that by killing and reloading the renderer. The GPU
watchdog already pauses across suspend for the same reason
(GpuWatchdogThread is a PowerSuspendObserver); the renderer hang monitor
has no power awareness.

WebContents::RendererUnresponsive() now checks base::PowerMonitor: when
the system is suspended, or resumed less than input::kHungRendererDelay
ago, it runs the hang monitor restarter it is handed instead of emitting
the event, so a renderer that is still stuck a full delay after the wake
is reported as before. The testing binding gains simulatePowerEvent() so
the spec can drive base::PowerMonitor's suspend/resume.
RenderWidgetHostImpl::WasHidden() stopped the hang monitor with
StopInputEventAckTimeout(), which also declares the renderer responsive.
A window that hangs and is then minimized, occluded or screen-locked
therefore emitted `responsive` without recovering, and the real recovery
later emitted nothing, so neither event could be trusted. Hiding now
pauses the timeout instead (folded into
refactor_unfilter_unresponsive_events.patch, which exists to make these
events usable from Electron); showing the window re-arms it and a real
input ack still emits `responsive`.
@codebytere
codebytere requested a review from a team as a code owner September 4, 2026 14:29
@codebytere codebytere added semver/patch backwards-compatible bug fixes target/44-x-y PR should also be added to the "44-x-y" branch. target/45-x-y PR should also be added to the "45-x-y" branch. labels Sep 4, 2026
@electron-cation electron-cation Bot added the new-pr 🌱 PR opened recently label Sep 4, 2026
…r-resume

# Conflicts:
#	shell/common/api/electron_api_testing.cc
@electron-cation electron-cation Bot removed the new-pr 🌱 PR opened recently label Sep 5, 2026
…r-resume

# Conflicts:
#	patches/chromium/revert_partial_remove_unused_prehandlemouseevent.patch
…r-resume

# Conflicts:
#	patches/chromium/revert_partial_remove_unused_prehandlemouseevent.patch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver/patch backwards-compatible bug fixes target/44-x-y PR should also be added to the "44-x-y" branch. target/45-x-y PR should also be added to the "45-x-y" branch.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants