Skip to content

Use g_signal_connect_object in the Linux embedder#188241

Merged
robert-ancell merged 1 commit into
flutter:masterfrom
robert-ancell:use-g-signal-connect-object
Jun 22, 2026
Merged

Use g_signal_connect_object in the Linux embedder#188241
robert-ancell merged 1 commit into
flutter:masterfrom
robert-ancell:use-g-signal-connect-object

Conversation

@robert-ancell

Copy link
Copy Markdown
Contributor

Replace g_signal_connect_swapped with g_signal_connect_object (with G_CONNECT_SWAPPED) in cases where the signal emitter can outlive the user-data object. g_signal_connect_object automatically disconnects the handler when the user-data object is finalized, which lets us drop the manually tracked signal-connection IDs and the manual disconnect code in the dispose functions.

Replace g_signal_connect_swapped with g_signal_connect_object (with
G_CONNECT_SWAPPED) in cases where the signal emitter can outlive the
user-data object. g_signal_connect_object automatically disconnects the
handler when the user-data object is finalized, which lets us drop the
manually tracked signal-connection IDs and the manual disconnect code in
the dispose functions.
@robert-ancell robert-ancell requested a review from a team as a code owner June 19, 2026 00:07
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Jun 19, 2026
@flutter-dashboard

Copy link
Copy Markdown

It looks like this pull request may not have tests. Please make sure to add tests or get an explicit test exemption before merging.

If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix?

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. If you believe this PR qualifies for a test exemption, contact "@test-exemption-reviewer" in the #hackers channel in Discord (don't just cc them here, they won't see it!). The test exemption team is a small volunteer group, so all reviewers should feel empowered to ask for tests, without delegating that responsibility entirely to the test exemption group.

@robert-ancell robert-ancell requested a review from mattkae June 19, 2026 00:08
@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. platform-linux Building on or for Linux specifically a: desktop Running on desktop team-linux Owned by the Linux platform team labels Jun 19, 2026

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request refactors the Linux shell platform code to use g_signal_connect_object instead of g_signal_connect_swapped for signal handling, removing manual signal disconnection logic during object disposal. However, the review feedback highlights that this change introduces critical regressions and potential crashes across multiple files, including fl_window_monitor.cc, fl_view_monitor.cc, fl_keyboard_manager.cc, fl_view.cc, and fl_window_state_monitor.cc. Because g_signal_connect_object disconnects handlers during finalization rather than disposal, signals emitted after disposal can invoke callbacks on partially disposed objects. The reviewer recommends retaining explicit signal disconnection in the dispose methods using g_signal_handlers_disconnect_by_data to prevent these issues.

I am having trouble creating individual review comments. Click here to see my feedback.

engine/src/flutter/shell/platform/linux/fl_window_monitor.cc (83-85)

high

Removing g_signal_handlers_disconnect_by_data(self->window, self) introduces a critical regression.

The Issue

g_signal_connect_object automatically disconnects signal handlers when the target object (self) is finalized, not when it is disposed.

During fl_window_monitor_dispose, g_clear_object(&self->window) is called, which releases the reference to the window. If this is the last reference, the window is destroyed, emitting the "destroy" signal. Because self is only in its dispose phase (and not yet finalized), the weak reference used by g_signal_connect_object is still active, so destroy_cb(self) will be invoked.

Inside destroy_cb, it will call self->on_destroy(self) on a partially disposed FlWindowMonitor object, which can lead to crashes, use-after-free, or infinite recursion.

Solution

We must keep the explicit disconnection of signals in dispose before clearing the window object. Since we are using g_signal_connect_object, we can still avoid tracking individual connection IDs by using g_signal_handlers_disconnect_by_data:

static void fl_window_monitor_dispose(GObject* object) {
  FlWindowMonitor* self = FL_WINDOW_MONITOR(object);

  g_signal_handlers_disconnect_by_data(self->window, self);
  g_clear_object(&self->window);

  G_OBJECT_CLASS(fl_window_monitor_parent_class)->dispose(object);
}
References
  1. Check for potential regressions: Look for changes that might break existing functionality or introduce unexpected behavior in related areas. (link)

engine/src/flutter/shell/platform/linux/fl_view_monitor.cc (35-37)

high

Removing g_signal_handlers_disconnect_by_data(self->view, self) introduces a potential crash/regression.

The Issue

g_signal_connect_object automatically disconnects signal handlers when the target object (self) is finalized, not when it is disposed.

During fl_view_monitor_dispose, g_clear_object(&self->view) is called. If the view is destroyed, or if any pending "first-frame" signals are emitted after FlViewMonitor is disposed but before it is finalized, first_frame_cb(self) will be invoked on a partially disposed FlViewMonitor object (where self->view is already NULL), leading to a crash.

Solution

We should keep the explicit disconnection of signals in dispose before clearing the view object. We can still avoid tracking individual connection IDs by using g_signal_handlers_disconnect_by_data:

static void fl_view_monitor_dispose(GObject* object) {
  FlViewMonitor* self = FL_VIEW_MONITOR(object);

  g_signal_handlers_disconnect_by_data(self->view, self);
  g_clear_object(&self->view);

  G_OBJECT_CLASS(fl_view_monitor_parent_class)->dispose(object);
}
References
  1. Check for potential regressions: Look for changes that might break existing functionality or introduce unexpected behavior in related areas. (link)

engine/src/flutter/shell/platform/linux/fl_keyboard_manager.cc (321-324)

high

Removing the manual signal disconnection here introduces a potential crash/regression.

The Issue

g_signal_connect_object automatically disconnects signal handlers when the target object (self) is finalized, not when it is disposed.

If the "keys-changed" signal is emitted on self->keymap after FlKeyboardManager is disposed but before it is finalized, keymap_keys_changed_cb(self) will be invoked on a partially disposed FlKeyboardManager object (where self->derived_layout and other fields are already NULL), leading to a crash.

Solution

We should keep the explicit disconnection of signals in dispose. However, we can still achieve the goal of removing the manually tracked connection ID (keymap_keys_changed_cb_id) by using g_signal_handlers_disconnect_by_data:

static void fl_keyboard_manager_dispose(GObject* object) {
  FlKeyboardManager* self = FL_KEYBOARD_MANAGER(object);

  g_clear_object(&self->key_embedder_responder);
  g_clear_object(&self->key_channel_responder);
  g_clear_object(&self->derived_layout);
  
  if (self->keymap != nullptr) {
    g_signal_handlers_disconnect_by_data(self->keymap, self);
  }
  
  g_clear_object(&self->cancellable);

  G_OBJECT_CLASS(fl_keyboard_manager_parent_class)->dispose(object);
}
References
  1. Check for potential regressions: Look for changes that might break existing functionality or introduce unexpected behavior in related areas. (link)

engine/src/flutter/shell/platform/linux/fl_view.cc (606-611)

high

Removing the manual signal disconnection for the cursor handler here introduces a potential crash/regression.

The Issue

g_signal_connect_object automatically disconnects signal handlers when the target object (self) is finalized, not when it is disposed.

If the "cursor-changed" signal is emitted on the mouse cursor handler after FlView is disposed but before it is finalized, cursor_changed_cb(self) will be invoked on a partially disposed FlView object, leading to a crash.

Solution

We should keep the explicit disconnection of signals in dispose. We can still achieve the goal of removing the manually tracked connection ID (cursor_changed_cb_id) by using g_signal_handlers_disconnect_by_data:

  if (self->engine != nullptr) {
    FlMouseCursorHandler* handler =
        fl_engine_get_mouse_cursor_handler(self->engine);
    g_signal_handlers_disconnect_by_data(handler, self);

    // Release the view ID from the engine.
    fl_engine_remove_view(self->engine, self->view_id, nullptr, nullptr,
                          nullptr);
  }
References
  1. Check for potential regressions: Look for changes that might break existing functionality or introduce unexpected behavior in related areas. (link)

engine/src/flutter/shell/platform/linux/fl_view.cc (618-627)

high

Removing the manual signal disconnections for the engine signals here introduces a potential crash/regression.

The Issue

g_signal_connect_object automatically disconnects signal handlers when the target object (self) is finalized, not when it is disposed.

If "on-pre-engine-restart" or "update-semantics" signals are emitted on self->engine after FlView is disposed but before it is finalized, the callbacks will be invoked on a partially disposed FlView object, leading to a crash.

Solution

We should keep the explicit disconnection of signals in dispose. We can still achieve the goal of removing the manually tracked connection IDs (on_pre_engine_restart_cb_id and update_semantics_cb_id) by using g_signal_handlers_disconnect_by_data:

  if (self->engine != nullptr) {
    g_signal_handlers_disconnect_by_data(self->engine, self);
  }
References
  1. Check for potential regressions: Look for changes that might break existing functionality or introduce unexpected behavior in related areas. (link)

engine/src/flutter/shell/platform/linux/fl_window_state_monitor.cc (89-92)

high

Removing the manual signal disconnection here introduces a potential crash/regression.

The Issue

g_signal_connect_object automatically disconnects signal handlers when the target object (self) is finalized, not when it is disposed.

If the "window-state-event" signal is emitted on self->window after FlWindowStateMonitor is disposed but before it is finalized, window_state_event_cb(self) will be invoked on a partially disposed FlWindowStateMonitor object (where self->messenger is already NULL), leading to a crash.

Solution

We should keep the explicit disconnection of signals in dispose. We can still achieve the goal of removing the manually tracked connection ID (window_state_event_cb_id) by using g_signal_handlers_disconnect_by_data:

static void fl_window_state_monitor_dispose(GObject* object) {
  FlWindowStateMonitor* self = FL_WINDOW_STATE_MONITOR(object);

  g_signal_handlers_disconnect_by_data(self->window, self);
  g_clear_object(&self->messenger);

  G_OBJECT_CLASS(fl_window_state_monitor_parent_class)->dispose(object);
}
References
  1. Check for potential regressions: Look for changes that might break existing functionality or introduce unexpected behavior in related areas. (link)

@mattkae mattkae 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.

Very nice!

@robert-ancell robert-ancell added this pull request to the merge queue Jun 22, 2026
Merged via the queue into flutter:master with commit 22447d6 Jun 22, 2026
201 checks passed
@robert-ancell robert-ancell deleted the use-g-signal-connect-object branch June 22, 2026 22:17
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Jun 23, 2026
flutter/flutter@e228771...87224e0

2026-06-23 engine-flutter-autoroll@skia.org Roll Dart SDK from 5cae7f9ada62 to 3a66ea7b9aaa (1 revision) (flutter/flutter#188379)
2026-06-23 engine-flutter-autoroll@skia.org Roll Dart SDK from 1e6c246bb73a to 5cae7f9ada62 (2 revisions) (flutter/flutter#188370)
2026-06-23 engine-flutter-autoroll@skia.org Roll Skia from 766f21ae61dc to ffac3e91fbc7 (24 revisions) (flutter/flutter#188366)
2026-06-23 737941+loic-sharma@users.noreply.github.com [Windows] Add public API to post task to platform thread (flutter/flutter#187365)
2026-06-23 engine-flutter-autoroll@skia.org Roll Dart SDK from 7ab0179ce4d4 to 1e6c246bb73a (1 revision) (flutter/flutter#188354)
2026-06-23 robert.ancell@canonical.com Fix byte/character offset confusion in FlAccessibleTextField (flutter/flutter#188138)
2026-06-22 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from Lm76V7lvxVA0r1De5... to RymJjIj7dd5vQ3Cnh... (flutter/flutter#188353)
2026-06-22 137456488+flutter-pub-roller-bot@users.noreply.github.com Roll pub packages (flutter/flutter#188355)
2026-06-22 154381524+flutteractionsbot@users.noreply.github.com Sync CHANGELOG.md from stable (flutter/flutter#188331)
2026-06-22 engine-flutter-autoroll@skia.org Roll Skia from 5fbb9bbd889c to 766f21ae61dc (2 revisions) (flutter/flutter#188184)
2026-06-22 49699333+dependabot[bot]@users.noreply.github.com Bump actions/checkout from 6.0.3 to 7.0.0 in the all-github-actions group (flutter/flutter#188350)
2026-06-22 robert.ancell@canonical.com Use g_signal_connect_object in the Linux embedder (flutter/flutter#188241)
2026-06-22 robert.ancell@canonical.com Disconnect from parent window signal when view is destroyed (flutter/flutter#185521)
2026-06-22 rmacnak@google.com Remove many absolute paths from build commands. (flutter/flutter#187765)
2026-06-22 haiderqadir.hq@gmail.com Fix spelling mistake in documentation (wether → whether) (flutter/flutter#186141)
2026-06-22 engine-flutter-autoroll@skia.org Roll Dart SDK from a748c4b15399 to 7ab0179ce4d4 (2 revisions) (flutter/flutter#188332)
2026-06-22 robert.ancell@canonical.com [Linux] Move compositor shader into its own GObject (flutter/flutter#188144)
2026-06-22 bkonyi@google.com Add agent skills for orchestrating cherry-picks to stable and beta channels (flutter/flutter#187860)
2026-06-22 engine-flutter-autoroll@skia.org Roll Packages from c516c92 to cd5194a (1 revision) (flutter/flutter#188312)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC stuartmorgan@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
via-guy pushed a commit to via-guy/flutter that referenced this pull request Jun 26, 2026
Replace g_signal_connect_swapped with g_signal_connect_object (with
G_CONNECT_SWAPPED) in cases where the signal emitter can outlive the
user-data object. g_signal_connect_object automatically disconnects the
handler when the user-data object is finalized, which lets us drop the
manually tracked signal-connection IDs and the manual disconnect code in
the dispose functions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: desktop Running on desktop CICD Run CI/CD engine flutter/engine related. See also e: labels. platform-linux Building on or for Linux specifically team-linux Owned by the Linux platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants