[nautilus/wip/coreyberla/window-dispose: 19/20] window: Don't finalize twice




commit bf563f37e149c76510362d1f855ba28f2c245639
Author: António Fernandes <antoniof gnome org>
Date:   Thu Sep 1 19:34:13 2022 +0100

    window: Don't finalize twice
    
    The pad controller owns a reference to the window (as an action group)
    and the window (as a widget) owns a reference to the pad controller.
    This is a reference cycle.
    
    Usually, reference cycles are resolved in dispose(), which can get
    called multiple times. However, GTK removes the controllers during
    finalize(). We end up calling finalize() recursively, which is a big
    problem.
    
    So our only option is to manually remove the controller before starting
    the destruction of the window.

 src/nautilus-window.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)
---
diff --git a/src/nautilus-window.c b/src/nautilus-window.c
index 5e1f9d9a5..9cccc3c18 100644
--- a/src/nautilus-window.c
+++ b/src/nautilus-window.c
@@ -126,6 +126,10 @@ struct _NautilusWindow
     gulong bookmarks_id;
 
     GQueue *tab_data_queue;
+
+    /* Pad controller which holds a reference to the window. Kept around to
+     * break reference-counting cycles during finalization. */
+    GtkPadController *pad_controller;
 };
 
 enum
@@ -1663,6 +1667,13 @@ nautilus_window_close (NautilusWindow *window)
     nautilus_window_save_geometry (window);
     nautilus_window_set_active_slot (window, NULL);
 
+    /* The pad controller hold a reference to the window, creating a cycle.
+     * Usually, reference cycles are resolved in dispose(), but GTK removes the
+     * controllers in finalize(), so our only option is to manually remove it
+     * here before starting the destruction of the window. */
+    gtk_widget_remove_controller (GTK_WIDGET (window),
+                                  GTK_EVENT_CONTROLLER (window->pad_controller));
+
     gtk_window_destroy (GTK_WINDOW (window));
 }
 
@@ -2051,7 +2062,6 @@ static void
 nautilus_window_init (NautilusWindow *window)
 {
     GtkWindowGroup *window_group;
-    GtkPadController *pad_controller;
     GtkEventController *controller;
 
     g_type_ensure (NAUTILUS_TYPE_TOOLBAR);
@@ -2084,11 +2094,15 @@ nautilus_window_init (NautilusWindow *window)
 
     window->tab_data_queue = g_queue_new ();
 
-    pad_controller = gtk_pad_controller_new (G_ACTION_GROUP (window), NULL);
-    gtk_pad_controller_set_action_entries (pad_controller,
+    /* Attention: this creates a reference cycle: the pad controller owns a
+     * reference to the window (as an action group) and the window (as a widget)
+     * owns a reference to the pad controller. To break this, we must remove
+     * the controller from the window before destroying the window. */
+    window->pad_controller = gtk_pad_controller_new (G_ACTION_GROUP (window), NULL);
+    gtk_pad_controller_set_action_entries (window->pad_controller,
                                            pad_actions, G_N_ELEMENTS (pad_actions));
     gtk_widget_add_controller (GTK_WIDGET (window),
-                               GTK_EVENT_CONTROLLER (pad_controller));
+                               GTK_EVENT_CONTROLLER (window->pad_controller));
 
     controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
     gtk_widget_add_controller (GTK_WIDGET (window), controller);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]