[gnome-builder/wip/chergert/debugger: 48/58] mi2: make listen/shutdown operations async
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/chergert/debugger: 48/58] mi2: make listen/shutdown operations async
- Date: Sun, 26 Mar 2017 07:25:23 +0000 (UTC)
commit c29b26119fc5c7fd9e81b88e4d1b72e2b866eb3e
Author: Christian Hergert <chergert redhat com>
Date: Sat Mar 25 17:19:11 2017 -0700
mi2: make listen/shutdown operations async
contrib/mi2/mi2-client.c | 174 ++++++++++++++++++++++++++++++++++----------
contrib/mi2/mi2-client.h | 16 ++++-
contrib/mi2/test-client.c | 24 +++++-
plugins/gdb/gdb_plugin.py | 12 ++-
4 files changed, 176 insertions(+), 50 deletions(-)
---
diff --git a/contrib/mi2/mi2-client.c b/contrib/mi2/mi2-client.c
index 412404c..adbc162 100644
--- a/contrib/mi2/mi2-client.c
+++ b/contrib/mi2/mi2-client.c
@@ -33,11 +33,9 @@ typedef struct
GIOStream *io_stream;
Mi2InputStream *input_stream;
Mi2OutputStream *output_stream;
- GCancellable *read_loop_cancellable;
+ GCancellable *listen_cancellable;
GQueue exec_tasks;
GQueue exec_commands;
-
- guint is_listening : 1;
} Mi2ClientPrivate;
enum {
@@ -107,7 +105,7 @@ mi2_client_check_ready (Mi2Client *self,
return FALSE;
}
- if (priv->read_loop_cancellable == NULL)
+ if (priv->listen_cancellable == NULL)
{
g_set_error (error,
G_IO_ERROR,
@@ -116,7 +114,7 @@ mi2_client_check_ready (Mi2Client *self,
return FALSE;
}
- if (g_cancellable_is_cancelled (priv->read_loop_cancellable))
+ if (g_cancellable_is_cancelled (priv->listen_cancellable))
{
g_set_error (error,
G_IO_ERROR,
@@ -173,12 +171,12 @@ mi2_client_dispose (GObject *object)
Mi2Client *self = (Mi2Client *)object;
Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
- mi2_client_cancel_all_tasks (self);
+ g_print ("*************** dispose\n");
g_clear_object (&priv->io_stream);
g_clear_object (&priv->input_stream);
g_clear_object (&priv->output_stream);
- g_clear_object (&priv->read_loop_cancellable);
+ g_clear_object (&priv->listen_cancellable);
G_OBJECT_CLASS (mi2_client_parent_class)->dispose (object);
}
@@ -186,6 +184,17 @@ mi2_client_dispose (GObject *object)
static void
mi2_client_finalize (GObject *object)
{
+ Mi2Client *self = (Mi2Client *)object;
+ Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+
+ g_assert (priv->exec_commands.length == 0);
+ g_assert (priv->exec_commands.head == NULL);
+ g_assert (priv->exec_commands.tail == NULL);
+
+ g_assert (priv->exec_tasks.length == 0);
+ g_assert (priv->exec_tasks.head == NULL);
+ g_assert (priv->exec_tasks.tail == NULL);
+
G_OBJECT_CLASS (mi2_client_parent_class)->finalize (object);
}
@@ -494,81 +503,166 @@ mi2_client_read_loop_cb (GObject *object,
gpointer user_data)
{
Mi2InputStream *stream = (Mi2InputStream *)object;
- g_autoptr(Mi2Client) self = user_data;
- Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+ g_autoptr(GTask) task = user_data;
g_autoptr(Mi2Message) message = NULL;
g_autoptr(GError) error = NULL;
+ GCancellable *cancellable;
+ Mi2Client *self;
g_assert (MI2_IS_INPUT_STREAM (stream));
- g_assert (MI2_IS_CLIENT (self));
+ g_assert (G_IS_ASYNC_RESULT (result));
+ g_assert (G_IS_TASK (task));
message = mi2_input_stream_read_message_finish (stream, result, &error);
+ g_assert (!message || MI2_IS_MESSAGE (message));
if (message == NULL)
{
- priv->is_listening = FALSE;
- g_clear_object (&priv->read_loop_cancellable);
- if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
- g_warning ("%s", error->message);
+ g_task_return_error (task, g_steal_pointer (&error));
return;
}
+ self = g_task_get_source_object (task);
+ g_assert (MI2_IS_CLIENT (self));
+
mi2_client_dispatch (self, message);
+ g_clear_object (&message);
- if (priv->is_listening)
- mi2_input_stream_read_message_async (priv->input_stream,
- priv->read_loop_cancellable,
- mi2_client_read_loop_cb,
- g_steal_pointer (&self));
+ if (g_task_return_error_if_cancelled (task))
+ return;
+
+ cancellable = g_task_get_cancellable (task);
+ mi2_input_stream_read_message_async (stream,
+ cancellable,
+ mi2_client_read_loop_cb,
+ g_steal_pointer (&task));
}
/**
- * mi2_client_start_listening:
+ * mi2_client_listen_async:
* @self: a #Mi2Client
+ * @cancellable: (nullable): an optional #GCancellable, or %NULL
+ * @callback: (scope async) (closure user_data): a callback to execute upon completion
+ * @user_data: the data to pass to @callback function
+ *
+ * Starts listening to the gdb process using the configured streams.
*
- * This function starts listening to the gdb process.
+ * This will hold a reference to the client (preventing finalization) until
+ * mi2_client_shutdown_async() has been called.
*
- * You should call this function after attaching to any signals you want
- * to be notified about so that you do not race with the gdb process to
- * handle those signals.
+ * Call mi2_client_listen_finish() from @callback to complete the operation.
*/
void
-mi2_client_start_listening (Mi2Client *self)
+mi2_client_listen_async (Mi2Client *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+ g_autoptr(GTask) task = NULL;
g_return_if_fail (MI2_IS_CLIENT (self));
- g_return_if_fail (priv->is_listening == FALSE);
- g_return_if_fail (priv->input_stream != NULL);
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ if (priv->listen_cancellable != NULL)
+ {
+ g_task_report_new_error (self, callback, user_data,
+ mi2_client_listen_async,
+ G_IO_ERROR,
+ G_IO_ERROR_INVAL,
+ "You are already listening");
+ return;
+ }
- priv->is_listening = TRUE;
- priv->read_loop_cancellable = g_cancellable_new ();
+ if (cancellable != NULL)
+ priv->listen_cancellable = g_object_ref (cancellable);
+ else
+ priv->listen_cancellable = g_cancellable_new ();
+
+ task = g_task_new (self, priv->listen_cancellable, callback, user_data);
+ g_task_set_source_tag (task, mi2_client_listen_async);
mi2_input_stream_read_message_async (priv->input_stream,
- priv->read_loop_cancellable,
+ priv->listen_cancellable,
mi2_client_read_loop_cb,
- g_object_ref (self));
+ g_steal_pointer (&task));
+}
+
+/**
+ * mi2_client_listen_finish:
+ * @self: a #Mi2Client
+ * @result: the #GAsyncResult provided to the callback function
+ * @error: a location for a #GError, or %NULL
+ *
+ * Completes an asynchronous request to mi2_client_listen_async().
+ *
+ * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
+ */
+gboolean
+mi2_client_listen_finish (Mi2Client *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (MI2_IS_CLIENT (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
}
/**
- * mi2_client_stop_listening:
+ * mi2_client_shutdown_async:
* @self: a #Mi2Client
+ * @cancellable: (nullable): an optional #GCancellable, or %NULL
+ * @callback: (scope async) (closure user_data): a callback to execute upon completion
+ * @user_data: the data to pass to @callback function
+ *
+ * Asynchronously requests the shutdown of the client, cancelling any in-flight
+ * requests and breaking the incoming event processing loop.
*
- * Stop listening to the gdb process and cancel any in-flight operations.
+ * Call mi2_client_shutdown_finish() to complete the operation.
*/
void
-mi2_client_stop_listening (Mi2Client *self)
+mi2_client_shutdown_async (Mi2Client *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
Mi2ClientPrivate *priv = mi2_client_get_instance_private (self);
+ g_autoptr(GTask) task = NULL;
g_return_if_fail (MI2_IS_CLIENT (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
- if (priv->is_listening)
- {
- priv->is_listening = FALSE;
- mi2_client_cancel_all_tasks (self);
- g_cancellable_cancel (priv->read_loop_cancellable);
- }
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_source_tag (task, mi2_client_shutdown_async);
+
+ mi2_client_cancel_all_tasks (self);
+
+ if (!g_cancellable_is_cancelled (priv->listen_cancellable))
+ g_cancellable_cancel (priv->listen_cancellable);
+
+ g_task_return_boolean (task, TRUE);
+}
+
+/**
+ * mi2_client_shutdown_finish:
+ * @self: a #Mi2Client
+ * @result: the #GAsyncResult provided to the callback function
+ * @error: a location for a #GError, or %NULL
+ *
+ * Completes an asynchronous request to mi2_client_shutdown_async().
+ *
+ * Returns: %TRUE if successful; otherwise %FALSE and @error is set.
+ */
+gboolean
+mi2_client_shutdown_finish (Mi2Client *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (MI2_IS_CLIENT (self), FALSE);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
}
static void
diff --git a/contrib/mi2/mi2-client.h b/contrib/mi2/mi2-client.h
index 818b960..af4b3be 100644
--- a/contrib/mi2/mi2-client.h
+++ b/contrib/mi2/mi2-client.h
@@ -86,8 +86,20 @@ gboolean mi2_client_exec_finish (Mi2Client
GAsyncResult *result,
Mi2ReplyMessage **reply,
GError **error);
-void mi2_client_start_listening (Mi2Client *self);
-void mi2_client_stop_listening (Mi2Client *self);
+void mi2_client_listen_async (Mi2Client *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mi2_client_listen_finish (Mi2Client *self,
+ GAsyncResult *result,
+ GError **error);
+void mi2_client_shutdown_async (Mi2Client *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mi2_client_shutdown_finish (Mi2Client *self,
+ GAsyncResult *result,
+ GError **error);
void mi2_client_continue_async (Mi2Client *self,
gboolean reverse,
GCancellable *cancellable,
diff --git a/contrib/mi2/test-client.c b/contrib/mi2/test-client.c
index 2bc9354..917de82 100644
--- a/contrib/mi2/test-client.c
+++ b/contrib/mi2/test-client.c
@@ -221,9 +221,8 @@ on_breakpoint_removed (Mi2Client *client,
{
g_print ("breakpoint removed: %d\n", breakpoint_id);
- mi2_client_stop_listening (client);
-
- g_timeout_add (100, (GSourceFunc)g_main_loop_quit, main_loop);
+ g_print ("Shutting down client\n");
+ mi2_client_shutdown_async (client, NULL, NULL, NULL);
}
static void
@@ -249,6 +248,23 @@ tty_done (GObject *object,
NULL);
}
+static void
+listen_done_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ g_autoptr(GError) error = NULL;
+ gboolean r;
+
+ g_print ("Listen operation completed\n");
+
+ r = mi2_client_listen_finish (MI2_CLIENT (object), result, &error);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_assert_cmpint (r, ==, FALSE);
+
+ g_main_loop_quit (main_loop);
+}
+
gint
main (gint argc,
gchar *argv[])
@@ -273,7 +289,7 @@ main (gint argc,
g_signal_connect (client, "breakpoint-inserted", G_CALLBACK (on_breakpoint_inserted), NULL);
g_signal_connect (client, "breakpoint-removed", G_CALLBACK (on_breakpoint_removed), NULL);
- mi2_client_start_listening (client);
+ mi2_client_listen_async (client, NULL, listen_done_cb, NULL);
cmd = g_strdup_printf ("-gdb-set inferior-tty %s", path);
mi2_client_exec_async (client, cmd, NULL, tty_done, NULL);
diff --git a/plugins/gdb/gdb_plugin.py b/plugins/gdb/gdb_plugin.py
index b824c15..ae79950 100644
--- a/plugins/gdb/gdb_plugin.py
+++ b/plugins/gdb/gdb_plugin.py
@@ -87,7 +87,7 @@ class GdbDebugger(Ide.Object, Ide.Debugger):
self.client.connect('log', self.on_client_log)
# Start the async read loop for the client to process replies.
- self.client.start_listening()
+ self.client.listen_async(None, self.on_listen_cb)
# We stole the pty from the runner so that we could pass it to gdb
# instead. This will ensure that gdb re-opens that pty. Since gdb is
@@ -116,10 +116,14 @@ class GdbDebugger(Ide.Object, Ide.Debugger):
except Exception as ex:
print(repr(ex))
+ def on_listen_cb(self, client, result):
+ try:
+ client.listen_finish(result)
+ except Exception as ex:
+ print(repr(ex))
+
def on_runner_exited(self, runner):
- client = self.client
- self.client = None
- client.stop_listening()
+ self.client.shutdown_async()
def on_client_log(self, client, message):
print('>>>', message[:-1])
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]