[gnome-builder] clang: try to reduce concurrent requests into a single



commit 104c1363dc0362b502da35faf4b875e0696bfd0e
Author: Christian Hergert <christian hergert me>
Date:   Thu Mar 26 21:54:03 2015 -0700

    clang: try to reduce concurrent requests into a single
    
    If we have a bunch of requests for the same file, just do the work once
    an then notify the others on the way out.
    
    This code is pretty hideous. It's also difficult to debug. What we
    *really* need is a nice abstraction that ties together with GTask to
    do this type of work.
    
    libiris had this, and *mabye* it makes sense to bring that back. Because
    then we could have just done an "iris_task_add_callback()" to propagate
    the result to additional tasks.

 libide/clang/ide-clang-service.c |  104 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 104 insertions(+), 0 deletions(-)
---
diff --git a/libide/clang/ide-clang-service.c b/libide/clang/ide-clang-service.c
index 819de6f..0b143c4 100644
--- a/libide/clang/ide-clang-service.c
+++ b/libide/clang/ide-clang-service.c
@@ -37,8 +37,15 @@ struct _IdeClangService
 
   GHashTable   *cached_units;
   GRWLock       cached_rwlock;
+  gpointer      padding1[6];
+
   CXIndex       index;
   GCancellable *cancellable;
+  gpointer      padding2[6];
+
+  GMutex        in_flight_lock;
+  GPtrArray    *in_flight;
+  GPtrArray    *waiting;
 };
 
 typedef struct
@@ -167,6 +174,37 @@ ide_clang_service_build_index (IdeClangService   *self,
 }
 
 static void
+ide_clang_service_notify_waiters_locked (IdeClangService         *self,
+                                         IdeFile                 *file,
+                                         IdeClangTranslationUnit *result)
+{
+  GList *tasks = NULL;
+  GList *iter;
+  gsize i;
+
+  g_assert (IDE_IS_CLANG_SERVICE (self));
+  g_assert (IDE_IS_FILE (file));
+  g_assert (IDE_IS_CLANG_TRANSLATION_UNIT (result));
+
+  for (i = 0; i < self->waiting->len; i++)
+    {
+      GTask *item = g_ptr_array_index (self->waiting, i);
+      IdeFile *item_file = g_task_get_task_data (item);
+
+      if (ide_file_equal (item_file, file))
+        tasks = g_list_prepend (tasks, g_object_ref (item));
+    }
+
+  for (iter = tasks; iter; iter = iter->next)
+    g_ptr_array_remove (self->waiting, iter->data);
+
+  for (iter = tasks; iter; iter = iter->next)
+    g_task_return_pointer (iter->data, g_object_ref (result), g_object_unref);
+
+  g_list_free_full (tasks, (GDestroyNotify)g_object_unref);
+}
+
+static void
 ide_clang_service_parse_worker (GTask        *task,
                                 gpointer      source_object,
                                 gpointer      task_data,
@@ -174,6 +212,7 @@ ide_clang_service_parse_worker (GTask        *task,
 {
   g_autoptr(IdeClangTranslationUnit) ret = NULL;
   g_autoptr(IdeHighlightIndex) index = NULL;
+  g_autoptr(IdeFile) file_copy = NULL;
   IdeClangService *self = source_object;
   CXTranslationUnit tu = NULL;
   ParseRequest *request = task_data;
@@ -269,9 +308,16 @@ ide_clang_service_parse_worker (GTask        *task,
                         g_object_ref (ret));
   g_rw_lock_writer_unlock (&self->cached_rwlock);
 
+  file_copy = g_object_ref (request->file);
+
   g_task_return_pointer (task, g_object_ref (ret), g_object_unref);
 
 cleanup:
+  g_mutex_lock (&self->in_flight_lock);
+  ide_clang_service_notify_waiters_locked (self, file_copy, ret);
+  g_ptr_array_remove (self->in_flight, task);
+  g_mutex_unlock (&self->in_flight_lock);
+
   g_array_unref (ar);
 }
 
@@ -316,6 +362,40 @@ ide_clang_service__get_build_flags_cb (GObject      *object,
   g_task_run_in_thread (task, ide_clang_service_parse_worker);
 }
 
+static gboolean
+ide_clang_service_attach_in_flight (IdeClangService *self,
+                                    IdeFile         *file,
+                                    GTask           *task)
+{
+  gboolean ret = FALSE;
+  gsize i;
+
+  g_assert (IDE_IS_CLANG_SERVICE (self));
+  g_assert (IDE_IS_FILE (file));
+  g_assert (G_IS_TASK (task));
+
+  g_mutex_lock (&self->in_flight_lock);
+
+  for (i = 0; i < self->in_flight->len; i++)
+    {
+      GTask *item = g_ptr_array_index (self->in_flight, i);
+      ParseRequest *request = g_task_get_task_data (item);
+
+      if (ide_file_equal (request->file, file))
+        {
+          g_task_set_task_data (task, g_object_ref (file), g_object_unref);
+          g_ptr_array_add (self->waiting, g_object_ref (task));
+          ret = TRUE;
+          goto unlock;
+        }
+    }
+
+unlock:
+  g_mutex_unlock (&self->in_flight_lock);
+
+  return ret;
+}
+
 /**
  * ide_clang_service_get_translation_unit_async:
  * @min_sequence: The minimum change sequence number to reuse a cached unit.
@@ -371,6 +451,9 @@ ide_clang_service_get_translation_unit_async (IdeClangService     *self,
         }
     }
 
+  if (ide_clang_service_attach_in_flight (self, file, task))
+    return;
+
   gfile = ide_file_get_file (file);
 
   if (!gfile || !(path = g_file_get_path (gfile)))
@@ -402,6 +485,10 @@ ide_clang_service_get_translation_unit_async (IdeClangService     *self,
 
   g_task_set_task_data (task, request, parse_request_free);
 
+  g_mutex_lock (&self->in_flight_lock);
+  g_ptr_array_add (self->in_flight, g_object_ref (task));
+  g_mutex_unlock (&self->in_flight_lock);
+
   /*
    * Request the build flags necessary to build this module from the build system.
    */
@@ -469,6 +556,8 @@ ide_clang_service_dispose (GObject *object)
 {
   IdeClangService *self = (IdeClangService *)object;
 
+  g_clear_pointer (&self->in_flight, g_ptr_array_unref);
+  g_clear_pointer (&self->waiting, g_ptr_array_unref);
   g_clear_pointer (&self->index, clang_disposeIndex);
   g_clear_object (&self->cancellable);
 
@@ -476,12 +565,24 @@ ide_clang_service_dispose (GObject *object)
 }
 
 static void
+ide_clang_service_finalize (GObject *object)
+{
+  IdeClangService *self = (IdeClangService *)object;
+
+  g_rw_lock_clear (&self->cached_rwlock);
+  g_mutex_clear (&self->in_flight_lock);
+
+  G_OBJECT_CLASS (ide_clang_service_parent_class)->finalize (object);
+}
+
+static void
 ide_clang_service_class_init (IdeClangServiceClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
   IdeServiceClass *service_class = IDE_SERVICE_CLASS (klass);
 
   object_class->dispose = ide_clang_service_dispose;
+  object_class->finalize = ide_clang_service_finalize;
 
   service_class->start = ide_clang_service_start;
   service_class->stop = ide_clang_service_stop;
@@ -491,11 +592,14 @@ static void
 ide_clang_service_init (IdeClangService *self)
 {
   g_rw_lock_init (&self->cached_rwlock);
+  g_mutex_init (&self->in_flight_lock);
 
   self->cached_units = g_hash_table_new_full ((GHashFunc)ide_file_hash,
                                               (GEqualFunc)ide_file_equal,
                                               g_object_unref,
                                               g_object_unref);
+  self->in_flight = g_ptr_array_new_with_free_func (g_object_unref);
+  self->waiting = g_ptr_array_new_with_free_func (g_object_unref);
 }
 
 /**


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