[gnome-builder] egg-task-cache: handle the cancellation in an idle callback
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] egg-task-cache: handle the cancellation in an idle callback
- Date: Tue, 7 Mar 2017 10:15:25 +0000 (UTC)
commit 32b5234dc22e159f22047391bc7822c0203efb06
Author: Debarshi Ray <debarshir gnome org>
Date: Tue Mar 7 10:55:54 2017 +0100
egg-task-cache: handle the cancellation in an idle callback
The caller's GTask was created in an earlier iteration of the
GMainLoop. Hence, the g_task_return_error_if_cancelled will complete
immediately, and there is a possibility that the GTask might be
destroyed before returning to the GMainLoop. If that happens, it will
deadlock because it would be disconnecting from the GCancellable
from the GCancellable::cancelled handler.
It is also not safe to avoid disconnecting from the GCancellable, in
case it was cancelled, because, if reset, it can emit the signal a
second time and we will be accessing invalid memory.
Hence the safest option is to handle the cancellation in an idle
callback and immediately returning to the GMainLoop in the "cancelled"
handler.
https://bugzilla.gnome.org/show_bug.cgi?id=779660
contrib/egg/egg-task-cache.c | 54 +++++++++++++++++++++++++++++++-----------
1 files changed, 40 insertions(+), 14 deletions(-)
---
diff --git a/contrib/egg/egg-task-cache.c b/contrib/egg/egg-task-cache.c
index 48e7c45..0e38ba7 100644
--- a/contrib/egg/egg-task-cache.c
+++ b/contrib/egg/egg-task-cache.c
@@ -235,13 +235,9 @@ cancelled_data_free (gpointer data)
g_clear_pointer (&cancelled->key, cancelled->self->key_destroy_func);
- if (cancelled->cancelled_id != 0)
- {
- g_cancellable_disconnect (cancelled->cancellable, cancelled->cancelled_id);
- cancelled->cancelled_id = 0;
-
- g_clear_object (&cancelled->cancellable);
- }
+ g_cancellable_disconnect (cancelled->cancellable, cancelled->cancelled_id);
+ cancelled->cancelled_id = 0;
+ g_clear_object (&cancelled->cancellable);
cancelled->self = NULL;
@@ -468,29 +464,28 @@ egg_task_cache_propagate_pointer (EggTaskCache *self,
}
}
-static void
-egg_task_cache_cancelled_cb (GCancellable *cancellable,
- gpointer user_data)
+static gboolean
+egg_task_cache_cancel_in_idle (gpointer user_data)
{
EggTaskCache *self;
CancelledData *data;
+ GCancellable *cancellable;
GPtrArray *queued;
GTask *task = user_data;
gboolean cancelled = FALSE;
- g_assert (G_IS_CANCELLABLE (cancellable));
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
+ cancellable = g_task_get_cancellable (task);
data = g_task_get_task_data (task);
g_assert (EGG_IS_TASK_CACHE (self));
+ g_assert (G_IS_CANCELLABLE (cancellable));
g_assert (data != NULL);
g_assert (data->self == self);
g_assert (data->cancellable == cancellable);
- data->cancelled_id = 0;
-
if ((queued = g_hash_table_lookup (self->queued, data->key)))
{
for (guint i = 0; i < queued->len; i++)
@@ -525,7 +520,38 @@ egg_task_cache_cancelled_cb (GCancellable *cancellable,
}
}
- g_return_if_fail (cancelled);
+ g_return_val_if_fail (cancelled, G_SOURCE_REMOVE);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+egg_task_cache_cancelled_cb (GCancellable *cancellable,
+ gpointer user_data)
+{
+ EggTaskCache *self;
+ CancelledData *data;
+ GMainContext *context;
+ g_autoptr(GSource) source = NULL;
+ GTask *task = user_data;
+
+ g_assert (G_IS_CANCELLABLE (cancellable));
+ g_assert (G_IS_TASK (task));
+
+ self = g_task_get_source_object (task);
+ data = g_task_get_task_data (task);
+
+ g_assert (EGG_IS_TASK_CACHE (self));
+ g_assert (data != NULL);
+ g_assert (data->self == self);
+ g_assert (data->cancellable == cancellable);
+
+ source = g_idle_source_new ();
+ g_source_set_callback (source, egg_task_cache_cancel_in_idle, g_object_ref (task), g_object_unref);
+ g_source_set_name (source, "[egg] egg_task_cache_cancel_in_idle");
+
+ context = g_main_context_get_thread_default ();
+ g_source_attach (source, context);
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]