From c155b170bcf644ab2cdf7b11afdda0d9c100772e Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Fri, 26 Apr 2013 12:45:57 -0400 Subject: 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 file changed, 32 insertions(+), 16 deletions(-) (limited to 'e-util') diff --git a/e-util/e-photo-cache.c b/e-util/e-photo-cache.c index 42bad99eb6..a0ccb12813 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 * -- cgit v1.2.3