Skip to content

fix: exit gracefully when the display server connection is lost - #52603

Merged
ckerr merged 6 commits into
mainfrom
linux-ozone-shutdown
Aug 7, 2026
Merged

fix: exit gracefully when the display server connection is lost#52603
ckerr merged 6 commits into
mainfrom
linux-ozone-shutdown

Conversation

@codebytere

@codebytere codebytere commented Aug 3, 2026

Copy link
Copy Markdown
Member

Description of Change

On Linux the Ozone platform runs the browser-provided shutdown callback when the X11/Wayland connection breaks (X server exit, session logout, compositor restart). Ours went straight to LOG(FATAL), so every display-server teardown produced a crash report. Chrome's equivalent runs chrome::SessionEnding() first and only fatals if that fails to end the process.

Add Browser::ExitWithCode() (the app.exit() path, callable from native code), have the callback force-exit like app.exit(0), and keep the fatal as a 10-second delayed watchdog for the case where exiting hangs.

Checklist

  • PR description included and stakeholders cc'd
  • npm test passes
  • tests are changed or added
  • relevant documentation, tutorials, templates and examples are changed or are not needed

Release Notes

Notes: Fixed a crash report when the X server or Wayland compositor exits while an app is running.

@codebytere codebytere added semver/patch backwards-compatible bug fixes target/42-x-y PR should also be added to the "42-x-y" branch. target/43-x-y PR should also be added to the "43-x-y" branch. target/44-x-y PR should also be added to the "44-x-y" branch. labels Aug 3, 2026
@electron-cation electron-cation Bot added the new-pr 🌱 PR opened recently label Aug 3, 2026
The Ozone shutdown callback that runs when the X11/Wayland connection
breaks went straight to LOG(FATAL), so every X server exit or
compositor restart produced a crash report. Add
Browser::ExitWithCode() (the app.exit() path, callable from native
code), have the callback force-exit like app.exit(0), and keep the
FATAL as a 10-second delayed watchdog in case exiting hangs.
@codebytere
codebytere force-pushed the linux-ozone-shutdown branch from d00223b to f913bf3 Compare August 3, 2026 03:01
Comment thread shell/browser/electron_browser_main_parts.cc Outdated

@jkleinsc jkleinsc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

#52603 (review) needs to be addressed.

@codebytere
codebytere requested a review from deepak1556 August 3, 2026 16:11
@codebytere
codebytere requested a review from jkleinsc August 3, 2026 18:41

@jkleinsc jkleinsc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks like there is a compile error:
The build fails in shell/browser/electron_browser_main_parts.cc with:

error: only virtual member functions can be marked 'override'
      void Alarm() override { LOG(FATAL) << "Failed to shutdown."; }

This PR adds a local ShutdownWatchdog class that subclasses base::Watchdog and overrides Alarm():

class ShutdownWatchdog : public base::Watchdog {
 public:
  ShutdownWatchdog()
      : base::Watchdog(base::Seconds(10), "OzoneShutdown", true) {}
  void Alarm() override { LOG(FATAL) << "Failed to shutdown."; }
};

But, base::Watchdog::Alarm() (base/threading/watchdog.h) is not virtual — only base::Watchdog::Delegate::Alarm() is:

class BASE_EXPORT Watchdog {
 public:
  class Delegate {
   public:
    virtual ~Delegate() = default;
    virtual void Alarm() = 0;   // <- this is the virtual one
  };
  ...
  void Alarm();   // <- non-virtual, dispatches to delegate_->Alarm() or DefaultAlarm()

In base/threading/watchdog.cc:

void Watchdog::Alarm() {
  if (delegate_) {
    delegate_->Alarm();
  } else {
    DefaultAlarm();
  }
}

So subclassing Watchdog and redefining Alarm() just hides the base method (and would never actually be invoked by the watchdog thread) — hence the compiler correctly rejects override since there's no virtual base method being overridden.

Suggested Fix

Instead of subclassing base::Watchdog, implement base::Watchdog::Delegate and pass it into the Watchdog constructor's delegate parameter:

auto shutdown_cb = base::BindOnce([] {
  class ShutdownWatchdogDelegate : public base::Watchdog::Delegate {
   public:
    void Alarm() override { LOG(FATAL) << "Failed to shutdown."; }
  };
  static base::NoDestructor<ShutdownWatchdogDelegate> delegate;
  static base::NoDestructor<base::Watchdog> watchdog(
      base::Seconds(10), "OzoneShutdown", /*enabled=*/true, delegate.get());
  watchdog->Arm();
  if (Browser* browser = Browser::Get())
    browser->ExitWithCode(content::RESULT_CODE_NORMAL_EXIT);
  else
    LOG(FATAL) << "Failed to shutdown.";
});

This uses the delegate pattern base::Watchdog actually supports for custom alarm behavior, rather than trying to override a non-virtual method.

Comment thread shell/browser/electron_browser_main_parts.cc

@ckerr ckerr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👍 on John's analysis wrt using the delegate if we need Alarm() to be fatal.

I'm not 100% sure though, does Alarm() need to be fatal? If so, John's suggestion is the one to use. If not, ...maybe we just remove the breaking Alarm() override call?

@codebytere
codebytere requested review from ckerr and jkleinsc August 4, 2026 01:46
Comment thread shell/browser/electron_browser_main_parts.cc Outdated

@deepak1556 deepak1556 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks!

@electron-cation electron-cation Bot removed the new-pr 🌱 PR opened recently label Aug 4, 2026
@codebytere
codebytere dismissed jkleinsc’s stale review August 7, 2026 00:17

Changes addressed

@ckerr
ckerr merged commit e752ef4 into main Aug 7, 2026
73 checks passed
@ckerr
ckerr deleted the linux-ozone-shutdown branch August 7, 2026 03:17
@release-clerk

release-clerk Bot commented Aug 7, 2026

Copy link
Copy Markdown

Release Notes Persisted

Fixed a crash report when the X server or Wayland compositor exits while an app is running.

@trop

trop Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

I have automatically backported this PR to "44-x-y", please check out #52684

@trop trop Bot added the in-flight/44-x-y label Aug 7, 2026
@trop

trop Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

I have automatically backported this PR to "43-x-y", please check out #52685

@trop trop Bot removed the target/44-x-y PR should also be added to the "44-x-y" branch. label Aug 7, 2026
@trop

trop Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

I have automatically backported this PR to "42-x-y", please check out #52686

@trop trop Bot added in-flight/43-x-y in-flight/42-x-y merged/42-x-y PR was merged to the "42-x-y" branch. merged/44-x-y PR was merged to the "44-x-y" branch. merged/43-x-y PR was merged to the "43-x-y" branch. and removed target/43-x-y PR should also be added to the "43-x-y" branch. target/42-x-y PR should also be added to the "42-x-y" branch. in-flight/42-x-y in-flight/44-x-y in-flight/43-x-y labels Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

45-x-y merged/42-x-y PR was merged to the "42-x-y" branch. merged/43-x-y PR was merged to the "43-x-y" branch. merged/44-x-y PR was merged to the "44-x-y" branch. semver/patch backwards-compatible bug fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants