diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2013-04-27 00:45:57 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2013-04-27 01:18:38 +0800 |
commit | c155b170bcf644ab2cdf7b11afdda0d9c100772e (patch) | |
tree | 294b72eb3a95d5a9214b8da2521ec38b1e5171e6 /e-util | |
parent | 26229dd082022160253ee6970f512246c6b83ae4 (diff) | |
download | gsoc2013-evolution-c155b170bcf644ab2cdf7b11afdda0d9c100772e.tar.gz gsoc2013-evolution-c155b170bcf644ab2cdf7b11afdda0d9c100772e.tar.zst gsoc2013-evolution-c155b170bcf644ab2cdf7b11afdda0d9c100772e.zip |
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.
Diffstat (limited to 'e-util')
-rw-r--r-- | 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 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 * |