[gnome-builder/wip/chergert/clang] clang: implement cancelRequest support in daemon



commit 8c1067a9638d2319c434c3c286f7898ace6d878a
Author: Christian Hergert <chergert redhat com>
Date:   Thu Apr 26 19:58:44 2018 -0700

    clang: implement cancelRequest support in daemon
    
    This allows us to cancel an in-flight operation across the process
    boundary.

 src/plugins/clang/gnome-builder-clang.c | 50 +++++++++++++++++++++++
 src/plugins/clang/ide-clang-client.c    | 70 ++++++++++++++++++++++++++++-----
 2 files changed, 111 insertions(+), 9 deletions(-)
---
diff --git a/src/plugins/clang/gnome-builder-clang.c b/src/plugins/clang/gnome-builder-clang.c
index 12f50d187..17a5b88b5 100644
--- a/src/plugins/clang/gnome-builder-clang.c
+++ b/src/plugins/clang/gnome-builder-clang.c
@@ -37,6 +37,7 @@
 static guint      in_flight;
 static gboolean   closing;
 static GMainLoop *main_loop;
+static GQueue     ops;
 
 /* Client Operations {{{1 */
 
@@ -46,6 +47,7 @@ typedef struct
   JsonrpcClient *client;
   GVariant      *id;
   GCancellable  *cancellable;
+  GList          link;
 } ClientOp;
 
 static void
@@ -96,6 +98,7 @@ client_op_unref (ClientOp *op)
       g_clear_object (&op->cancellable);
       g_clear_object (&op->client);
       g_clear_pointer (&op->id, g_variant_unref);
+      g_queue_unlink (&ops, &op->link);
       g_slice_free (ClientOp, op);
 
       in_flight--;
@@ -118,6 +121,9 @@ client_op_new (JsonrpcClient *client,
   op->client = g_object_ref (client);
   op->cancellable = g_cancellable_new ();
   op->ref_count = 1;
+  op->link.data = op;
+
+  g_queue_push_tail_link (&ops, &op->link);
 
   ++in_flight;
 
@@ -812,6 +818,49 @@ handle_initialize (JsonrpcServer *server,
   client_op_reply (op, NULL);
 }
 
+/* Cancel Request {{{1 */
+
+static void
+handle_cancel_request (JsonrpcServer *server,
+                       JsonrpcClient *client,
+                       const gchar   *method,
+                       GVariant      *id,
+                       GVariant      *params,
+                       IdeClang      *clang)
+{
+  g_autoptr(ClientOp) op = NULL;
+  g_autoptr(GVariant) cid = NULL;
+
+  g_assert (JSONRPC_IS_SERVER (server));
+  g_assert (JSONRPC_IS_CLIENT (client));
+  g_assert (g_str_equal (method, "$/cancelRequest"));
+  g_assert (id != NULL);
+  g_assert (IDE_IS_CLANG (clang));
+
+  op = client_op_new (client, id);
+
+  if (params == NULL || !(cid = g_variant_lookup_value (params, "id", NULL)))
+    {
+      client_op_bad_params (op);
+      return;
+    }
+
+  /* Lookup in-flight command to cancel it */
+
+  for (const GList *iter = ops.head; iter != NULL; iter = iter->next)
+    {
+      ClientOp *ele = iter->data;
+
+      if (g_variant_equal (ele->id, cid))
+        {
+          g_cancellable_cancel (ele->cancellable);
+          break;
+        }
+    }
+
+  client_op_reply (op, NULL);
+}
+
 /* Main and Server Setup {{{1 */
 
 static void
@@ -881,6 +930,7 @@ main (gint argc,
   ADD_HANDLER ("clang/locateSymbol", handle_locate_symbol);
   ADD_HANDLER ("clang/getHighlightIndex", handle_get_highlight_index);
   ADD_HANDLER ("clang/setBuffer", handle_set_buffer);
+  ADD_HANDLER ("$/cancelRequest", handle_cancel_request);
 
 #undef ADD_HANDLER
 
diff --git a/src/plugins/clang/ide-clang-client.c b/src/plugins/clang/ide-clang-client.c
index d82e7c8a5..c02e988a1 100644
--- a/src/plugins/clang/ide-clang-client.c
+++ b/src/plugins/clang/ide-clang-client.c
@@ -48,8 +48,12 @@ enum {
 
 typedef struct
 {
-  gchar    *method;
-  GVariant *params;
+  IdeClangClient *self;
+  GCancellable   *cancellable;
+  gchar          *method;
+  GVariant       *params;
+  GVariant       *id;
+  gulong          cancel_id;
 } Call;
 
 G_DEFINE_TYPE_EXTENDED (IdeClangClient, ide_clang_client, IDE_TYPE_OBJECT, 0,
@@ -60,8 +64,16 @@ call_free (gpointer data)
 {
   Call *c = data;
 
+  if (c->cancel_id != 0)
+    g_cancellable_disconnect (c->cancellable, c->cancel_id);
+
+  c->cancel_id = 0;
+
   g_clear_pointer (&c->method, g_free);
   g_clear_pointer (&c->params, g_variant_unref);
+  g_clear_pointer (&c->id, g_variant_unref);
+  g_clear_object (&c->cancellable);
+  g_clear_object (&c->self);
   g_slice_free (Call, c);
 }
 
@@ -144,6 +156,7 @@ ide_clang_client_subprocess_exited (IdeClangClient          *self,
   if (self->state == STATE_RUNNING)
     self->state = STATE_SPAWNING;
 
+  g_clear_object (&self->rpc_client);
   g_clear_pointer (&self->seq_by_file, g_hash_table_unref);
 
   IDE_EXIT;
@@ -168,6 +181,7 @@ ide_clang_client_subprocess_spawned (IdeClangClient          *self,
   g_assert (IDE_IS_CLANG_CLIENT (self));
   g_assert (IDE_IS_SUBPROCESS (subprocess));
   g_assert (IDE_IS_SUBPROCESS_SUPERVISOR (supervisor));
+  g_assert (self->rpc_client == NULL);
 
   if (self->state == STATE_SPAWNING)
     self->state = STATE_RUNNING;
@@ -185,7 +199,6 @@ ide_clang_client_subprocess_spawned (IdeClangClient          *self,
   fd = g_unix_output_stream_get_fd (G_UNIX_OUTPUT_STREAM (output));
   g_unix_set_fd_nonblocking (fd, TRUE, NULL);
 
-  g_clear_object (&self->rpc_client);
   self->rpc_client = jsonrpc_client_new (stream);
   jsonrpc_client_set_use_gvariant (self->rpc_client, TRUE);
 
@@ -472,17 +485,46 @@ ide_clang_client_call_get_client_cb (GObject      *object,
       return;
     }
 
+  if (ide_task_return_error_if_cancelled (task))
+    return;
+
   call = ide_task_get_task_data (task);
 
   g_assert (call != NULL);
   g_assert (call->method != NULL);
 
-  jsonrpc_client_call_async (client,
-                             call->method,
-                             call->params,
-                             ide_task_get_cancellable (task),
-                             ide_clang_client_call_cb,
-                             g_object_ref (task));
+  jsonrpc_client_call_with_id_async (client,
+                                     call->method,
+                                     call->params,
+                                     &call->id,
+                                     ide_task_get_cancellable (task),
+                                     ide_clang_client_call_cb,
+                                     g_object_ref (task));
+}
+
+static void
+ide_clang_client_call_cancelled (GCancellable *cancellable,
+                                 Call         *call)
+{
+  g_autoptr(GVariant) params = NULL;
+  GVariantDict dict;
+
+  g_assert (G_IS_CANCELLABLE (cancellable));
+  g_assert (call != NULL);
+  g_assert (call->cancellable == cancellable);
+  g_assert (call->cancel_id != 0);
+  g_assert (IDE_IS_CLANG_CLIENT (call->self));
+
+  if (call->self->rpc_client == NULL)
+    return;
+
+  g_variant_dict_init (&dict, NULL);
+  g_variant_dict_insert_value (&dict, "id", call->id);
+
+  ide_clang_client_call_async (call->self,
+                               "$/cancelRequest",
+                               g_variant_dict_end (&dict),
+                               NULL, NULL, NULL);
 }
 
 void
@@ -501,6 +543,7 @@ ide_clang_client_call_async (IdeClangClient      *self,
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   call = g_slice_new0 (Call);
+  call->self = g_object_ref (self);
   call->method = g_strdup (method);
   call->params = g_variant_ref_sink (params);
 
@@ -508,6 +551,15 @@ ide_clang_client_call_async (IdeClangClient      *self,
   ide_task_set_source_tag (task, ide_clang_client_call_async);
   ide_task_set_task_data (task, call, call_free);
 
+  if (cancellable != NULL)
+    {
+      call->cancellable = g_object_ref (cancellable);
+      call->cancel_id = g_cancellable_connect (cancellable,
+                                               G_CALLBACK (ide_clang_client_call_cancelled),
+                                               call,
+                                               NULL);
+    }
+
   ide_clang_client_get_client_async (self,
                                      cancellable,
                                      ide_clang_client_call_get_client_cb,


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