[evolution] EPhotoCache: Fix deadlock when cancelling subtasks.



commit c155b170bcf644ab2cdf7b11afdda0d9c100772e
Author: Matthew Barnes <mbarnes redhat com>
Date:   Fri Apr 26 12:45:57 2013 -0400

    EPhotoCache: Fix deadlock when cancelling subtasks.
    
    This seems to have started with the gravatar module.  SoupRequest is
    apparently quite eager to cancel HTTP requests -- so much so that we
    need to slow it down within EPhotoCache, so we don't wind up calling
    g_cancellable_disconnect() during a GCancellable signal emission.

 e-util/e-photo-cache.c |   48 ++++++++++++++++++++++++++++++++----------------
 1 files changed, 32 insertions(+), 16 deletions(-)
---
diff --git a/e-util/e-photo-cache.c b/e-util/e-photo-cache.c
index 42bad99..a0ccb12 100644
--- a/e-util/e-photo-cache.c
+++ b/e-util/e-photo-cache.c
@@ -177,12 +177,14 @@ async_subtask_unref (AsyncSubtask *async_subtask)
        }
 }
 
-static void
-async_subtask_cancel (AsyncSubtask *async_subtask)
+static gboolean
+async_subtask_cancel_idle_cb (gpointer user_data)
 {
-       g_return_if_fail (async_subtask != NULL);
+       AsyncSubtask *async_subtask = user_data;
 
        g_cancellable_cancel (async_subtask->cancellable);
+
+       return FALSE;
 }
 
 static gint
@@ -354,23 +356,37 @@ async_context_free (AsyncContext *async_context)
 static void
 async_context_cancel_subtasks (AsyncContext *async_context)
 {
-       GQueue queue;
-       AsyncSubtask *async_subtask;
+       GMainContext *main_context;
+       GList *list, *link;
+
+       main_context = g_main_context_ref_thread_default ();
 
-       /* Copy subtasks to a secondary GQueue to avoid invoking
-        * "cancelled" signal handlers while the mutex is locked. */
        g_mutex_lock (&async_context->lock);
-       queue.head = g_hash_table_get_keys (async_context->subtasks);
-       queue.tail = g_list_last (queue.head);
-       queue.length = g_list_length (queue.head);
-       g_queue_foreach (&queue, (GFunc) async_subtask_ref, NULL);
-       g_mutex_unlock (&async_context->lock);
 
-       /* Cancel all subtasks. */
-       while ((async_subtask = g_queue_pop_head (&queue)) != NULL) {
-               async_subtask_cancel (async_subtask);
-               async_subtask_unref (async_subtask);
+       list = g_hash_table_get_keys (async_context->subtasks);
+
+       /* XXX Cancel subtasks from idle callbacks to make sure we don't
+        *     finalize the GSimpleAsyncResult during a "cancelled" signal
+        *     emission from the main task's GCancellable.  That will make
+        *     g_cancellable_disconnect() in async_context_free() deadlock. */
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               AsyncSubtask *async_subtask = link->data;
+               GSource *idle_source;
+
+               idle_source = g_idle_source_new ();
+               g_source_set_priority (idle_source, G_PRIORITY_HIGH_IDLE);
+               g_source_set_callback (
+                       idle_source,
+                       async_subtask_cancel_idle_cb,
+                       async_subtask_ref (async_subtask),
+                       (GDestroyNotify) async_subtask_unref);
+               g_source_attach (idle_source, main_context);
+               g_source_unref (idle_source);
        }
+
+       g_list_free (list);
+
+       g_main_context_unref (main_context);
 }
 
 static DataCaptureClosure *


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