[gnome-builder] egg-task-cache: Fix cancellation when tasks are queued for a given key



commit f8762c97bc29cd8d8a28951d2c6ce5656b675e4a
Author: Debarshi Ray <debarshir gnome org>
Date:   Mon Mar 6 20:06:22 2017 +0100

    egg-task-cache: Fix cancellation when tasks are queued for a given key
    
    The callers' GCancellables are monitored, and whenever one is cancelled
    it is removed from the queue. A separate GCancellable is used with the
    in-flight GTask that fetches the value. Fetching is aborted if, and
    only if, all the queued tasks for a given key have been cancelled.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=779660

 contrib/egg/egg-task-cache.c |  112 +++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 111 insertions(+), 1 deletions(-)
---
diff --git a/contrib/egg/egg-task-cache.c b/contrib/egg/egg-task-cache.c
index fed4985..e8c3fff 100644
--- a/contrib/egg/egg-task-cache.c
+++ b/contrib/egg/egg-task-cache.c
@@ -34,6 +34,14 @@ typedef struct
 
 typedef struct
 {
+  EggTaskCache *self;
+  GCancellable *cancellable;
+  gpointer      key;
+  gulong        cancelled_id;
+} CancelledData;
+
+typedef struct
+{
   GSource  source;
   EggHeap *heap;
 } EvictSource;
@@ -220,6 +228,40 @@ cache_item_new (EggTaskCache  *self,
   return ret;
 }
 
+static void
+cancelled_data_free (gpointer data)
+{
+  CancelledData *cancelled = data;
+
+  cancelled->self->key_destroy_func (cancelled->key);
+  cancelled->key = NULL;
+
+  g_cancellable_disconnect (cancelled->cancellable, cancelled->cancelled_id);
+  g_clear_object (&cancelled->cancellable);
+  cancelled->cancelled_id = 0;
+
+  cancelled->self = NULL;
+
+  g_slice_free (CancelledData, cancelled);
+}
+
+static CancelledData *
+cancelled_data_new (EggTaskCache  *self,
+                    GCancellable  *cancellable,
+                    gconstpointer  key,
+                    gulong         cancelled_id)
+{
+  CancelledData *ret;
+
+  ret = g_slice_new0 (CancelledData);
+  ret->self = self;
+  ret->cancellable = (cancellable != NULL) ? g_object_ref (cancellable) : NULL;
+  ret->key = self->key_copy_func ((gpointer)key);
+  ret->cancelled_id = cancelled_id;
+
+  return ret;
+}
+
 static gpointer
 egg_task_cache_dummy_copy_func (gpointer boxed)
 {
@@ -424,6 +466,58 @@ egg_task_cache_propagate_pointer (EggTaskCache  *self,
 }
 
 static void
+egg_task_cache_cancelled_cb (GCancellable *cancellable,
+                             gpointer      user_data)
+{
+  EggTaskCache *self;
+  CancelledData *data;
+  GPtrArray *queued;
+  GTask *task = (GTask *)user_data;
+  gboolean cancelled = FALSE;
+
+  self = (EggTaskCache *)g_task_get_source_object (task);
+  data = (CancelledData *)g_task_get_task_data (task);
+
+  if ((queued = g_hash_table_lookup (self->queued, data->key)))
+    {
+      gsize i;
+
+      for (i = 0; i < queued->len; i++)
+        {
+          GCancellable *queued_cancellable;
+          GTask *queued_task;
+
+          queued_task = g_ptr_array_index (queued, i);
+          queued_cancellable = g_task_get_cancellable (queued_task);
+
+          if (queued_task == task && queued_cancellable == cancellable)
+            {
+              cancelled = g_task_return_error_if_cancelled (task);
+              g_ptr_array_remove_index_fast (queued, (guint) i);
+
+              EGG_COUNTER_DEC (queued);
+              break;
+            }
+        }
+
+      if (queued->len == 0)
+        {
+          GTask *fetch_task;
+
+          if ((fetch_task = g_hash_table_lookup (self->in_flight, data->key)))
+            {
+              GCancellable *fetch_cancellable;
+
+              fetch_cancellable = g_task_get_cancellable (fetch_task);
+              g_cancellable_cancel (fetch_cancellable);
+            }
+        }
+    }
+
+  g_return_if_fail (cancelled);
+}
+
+static void
 egg_task_cache_fetch_cb (GObject      *object,
                          GAsyncResult *result,
                          gpointer      user_data)
@@ -468,8 +562,10 @@ egg_task_cache_get_async (EggTaskCache        *self,
 {
   g_autoptr(GTask) fetch_task = NULL;
   g_autoptr(GTask) task = NULL;
+  CancelledData *data;
   GPtrArray *queued;
   gpointer ret;
+  gulong cancelled_id = 0;
 
   g_return_if_fail (EGG_IS_TASK_CACHE (self));
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
@@ -511,8 +607,11 @@ egg_task_cache_get_async (EggTaskCache        *self,
    */
   if (!g_hash_table_contains (self->in_flight, key))
     {
+      g_autoptr(GCancellable) fetch_cancellable = NULL;
+
+      fetch_cancellable = g_cancellable_new ();
       fetch_task = g_task_new (self,
-                               cancellable,
+                               fetch_cancellable,
                                egg_task_cache_fetch_cb,
                                self->key_copy_func ((gpointer)key));
       g_hash_table_insert (self->in_flight,
@@ -520,6 +619,17 @@ egg_task_cache_get_async (EggTaskCache        *self,
                            g_object_ref (fetch_task));
     }
 
+  if (cancellable != NULL)
+    {
+      cancelled_id = g_cancellable_connect (cancellable,
+                                            G_CALLBACK (egg_task_cache_cancelled_cb),
+                                            task,
+                                            NULL);
+    }
+
+  data = cancelled_data_new (self, cancellable, key, cancelled_id);
+  g_task_set_task_data (task, data, cancelled_data_free);
+
   if (fetch_task != NULL)
     {
       self->populate_callback (self,


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