[jsonrpc-glib] client: defer task completion to idle under panic
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [jsonrpc-glib] client: defer task completion to idle under panic
- Date: Wed, 17 Jan 2018 11:17:23 +0000 (UTC)
commit 2f46849d84a64673810f91a123f2f9b40b784794
Author: Christian Hergert <chergert redhat com>
Date: Wed Jan 17 03:17:00 2018 -0800
client: defer task completion to idle under panic
When we are in a panic situation, we want to avoid completing the task
immediately because it can get us into an immediate callback situation
which could have re-entrancy implications.
Just defer them to the main loop to avoid that case entirely.
src/jsonrpc-client.c | 60 +++++++++++++++++++++++++++++++++++++---------------
1 file changed, 43 insertions(+), 17 deletions(-)
---
diff --git a/src/jsonrpc-client.c b/src/jsonrpc-client.c
index 87d5aad..a3c6618 100644
--- a/src/jsonrpc-client.c
+++ b/src/jsonrpc-client.c
@@ -133,6 +133,12 @@ typedef struct
guint use_gvariant : 1;
} JsonrpcClientPrivate;
+typedef struct
+{
+ GHashTable *invocations;
+ GError *error;
+} PanicData;
+
G_DEFINE_TYPE_WITH_PRIVATE (JsonrpcClient, jsonrpc_client, G_TYPE_OBJECT)
enum {
@@ -212,6 +218,28 @@ is_jsonrpc_call (GVariantDict *dict)
g_variant_dict_contains (dict, "params"));
}
+static gboolean
+error_invocations_from_idle (gpointer data)
+{
+ PanicData *pd = data;
+ GHashTableIter iter;
+ GTask *task;
+
+ g_assert (pd != NULL);
+ g_assert (pd->invocations != NULL);
+ g_assert (pd->error != NULL);
+
+ g_hash_table_iter_init (&iter, pd->invocations);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&task))
+ g_task_return_error (task, g_error_copy (pd->error));
+
+ g_clear_pointer (&pd->invocations, g_hash_table_unref);
+ g_clear_pointer (&pd->error, g_error_free);
+ g_slice_free (PanicData, pd);
+
+ return G_SOURCE_REMOVE;
+}
+
/*
* jsonrpc_client_panic:
*
@@ -223,19 +251,27 @@ jsonrpc_client_panic (JsonrpcClient *self,
const GError *error)
{
JsonrpcClientPrivate *priv = jsonrpc_client_get_instance_private (self);
- g_autoptr(GHashTable) invocations = NULL;
- GHashTableIter iter;
- GTask *task;
+ g_autoptr(JsonrpcClient) hold = NULL;
+ PanicData *pd;
g_assert (JSONRPC_IS_CLIENT (self));
g_assert (error != NULL);
- g_object_ref (self);
+ hold = g_object_ref (self);
priv->failed = TRUE;
- /* Steal the tasks so that we don't have to worry about reentry. */
- invocations = g_steal_pointer (&priv->invocations);
+ /*
+ * Defer the completion of all tasks (with errors) until we've made it
+ * back to the main loop. Otherwise, we get into difficult to determine
+ * re-entrancy cases.
+ */
+ pd = g_slice_new0 (PanicData);
+ pd->invocations = g_steal_pointer (&priv->invocations);
+ pd->error = g_error_copy (error);
+ g_idle_add_full (G_PRIORITY_LOW, error_invocations_from_idle, pd, NULL);
+
+ /* Keep a hashtable around for code that expects a pointer there */
priv->invocations = g_hash_table_new_full (NULL, NULL, NULL, g_object_unref);
/* Now close the connection */
@@ -248,17 +284,7 @@ jsonrpc_client_panic (JsonrpcClient *self,
g_clear_object (&priv->input_stream);
g_clear_object (&priv->output_stream);
- /*
- * Now notify all of the in-flight invocations that they failed due
- * to an unrecoverable error.
- */
- g_hash_table_iter_init (&iter, invocations);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&task))
- g_task_return_error (task, g_error_copy (error));
-
- g_signal_emit (self, signals [FAILED], 0);
-
- g_object_unref (self);
+ g_signal_emit (hold, signals [FAILED], 0);
}
/*
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]