From a1bc1ae54d2330e32eac7b4df7c07393df87e7da Mon Sep 17 00:00:00 2001 From: Debarshi Ray Date: Thu, 4 Oct 2018 11:28:15 +0200 Subject: [PATCH 1/2] online-accounts: Track the lifecycle of CcGoaPanel across async calls Due to an API bug in GNOME Online Accounts, the asynchronous goa_provider_get_all method doesn't accept a GCancellable argument. This makes it difficult to cancel an ongoing call when the CcGoaPanel gets destroyed. Prior to commit c26f8ae018900a55, this was hacked around by taking a reference on the panel for the duration of the call. Instead of cancelling a pending call on destruction, it would keep the panel alive until the call was over. However, that was lost during commit c26f8ae018900a55. One thing to bear in mind is that GtkWidgets, CcGoaPanel is one, can be destroyed by a gtk_widget_destroy call, which is subtly different than a simple sequence of g_object_unref calls. When gtk_widget_destroy is used, it invokes the GObject::dispose virtual method of the widget. It is expected this will cause anything holding a reference to this widget to drop their references, leading to GObject::finalize being called. However, there is no guarantee that this will happen in the same iteration of the GMainLoop. Therefore, it is possible that when the goa_provider_get_all call finishes, the CcGoaPanel might be in a disposed, but not yet finalized state. When a GObject is in a disposed-but-not-finalized state, only a very limited number of operations can be performed on it. Its reference count can be altered, the memory used by the instance struct can be accessed, but none of the member GObjects can be assumed to be valid. eg., it's definitely illegal to add new rows to the member GtkListBox. Hence a boolean flag is used to mark the destroyed state of the panel. This second part is a small improvement over the earlier hack. https://gitlab.gnome.org/GNOME/gnome-control-center/issues/208 (cherry picked from commit 83558429b3f98a4bf4ada17b64f09f073a6b8c38) --- panels/online-accounts/cc-online-accounts-panel.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/panels/online-accounts/cc-online-accounts-panel.c b/panels/online-accounts/cc-online-accounts-panel.c index edd140e4b..1640c74c4 100644 --- a/panels/online-accounts/cc-online-accounts-panel.c +++ b/panels/online-accounts/cc-online-accounts-panel.c @@ -56,6 +56,7 @@ struct _CcGoaPanel GtkWidget *stack; GtkWidget *accounts_vbox; + gboolean destroyed; guint remove_account_timeout_id; }; @@ -401,6 +402,8 @@ cc_goa_panel_dispose (GObject *object) /* Must be destroyed in dispose, not finalize. */ g_clear_pointer (&panel->edit_account_dialog, gtk_widget_destroy); + panel->destroyed = TRUE; + G_OBJECT_CLASS (cc_goa_panel_parent_class)->dispose (object); } @@ -480,7 +483,7 @@ cc_goa_panel_init (CcGoaPanel *panel) panel); fill_accounts_listbox (panel); - goa_provider_get_all (get_all_providers_cb, panel); + goa_provider_get_all (get_all_providers_cb, g_object_ref_sink (panel)); gtk_widget_show (GTK_WIDGET (panel)); } @@ -843,7 +846,7 @@ get_all_providers_cb (GObject *source, GAsyncResult *res, gpointer user_data) { - CcGoaPanel *self = user_data; + g_autoptr (CcGoaPanel) self = user_data; GList *providers; GList *l; @@ -851,6 +854,9 @@ get_all_providers_cb (GObject *source, if (!goa_provider_get_all_finish (&providers, res, NULL)) return; + if (self->destroyed) + return; + for (l = providers; l != NULL; l = l->next) { GoaProvider *provider; -- 2.17.0