summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2009-06-25 20:57:49 +0200
committerAlexander Larsson <alexl@redhat.com>2009-06-25 21:00:38 +0200
commit36d06a5fb2ff170fb2388d0afc82df954383b453 (patch)
tree9f62a3fb21b2454f17f19ef0a5743460be3784cd /client
parentb548be8088f83b8cdeb66198e6092edfa518613d (diff)
downloadgvfs-36d06a5fb2ff170fb2388d0afc82df954383b453.tar.gz
Avoid deadlock on cancelling async enumerate_children
We can't call g_cancellable_disconnect from inside the cancel signal emission, as that deadlocks. However we know if that happens and can use the regular disconnect. Also, we need to grab the infos lock when calling trigger_async_done, this was only done in some cases, now its always done.
Diffstat (limited to 'client')
-rw-r--r--client/gdaemonfileenumerator.c25
1 files changed, 23 insertions, 2 deletions
diff --git a/client/gdaemonfileenumerator.c b/client/gdaemonfileenumerator.c
index 7d5053af..1b6b084e 100644
--- a/client/gdaemonfileenumerator.c
+++ b/client/gdaemonfileenumerator.c
@@ -228,8 +228,24 @@ trigger_async_done (GDaemonFileEnumerator *daemon, gboolean ok)
GList *rest, *l;
if (daemon->cancelled_tag != 0)
- g_cancellable_disconnect (daemon->async_cancel,
- daemon->cancelled_tag);
+ {
+ /* If ok, we're a normal callback on the main thread,
+ ensure protection against a thread cancelling and
+ running the callback again.
+ If !ok then we're in a callback which may be
+ from another thread, but we're guaranteed that
+ cancellation will only happen once. However
+ we can't use g_cancellable_disconnect, as this
+ deadlocks if we call it from within the signal
+ handler.
+ */
+ if (ok)
+ g_cancellable_disconnect (daemon->async_cancel,
+ daemon->cancelled_tag);
+ else
+ g_signal_handler_disconnect (daemon->async_cancel,
+ daemon->cancelled_tag);
+ }
/* cancelled signal handler won't execute after this */
@@ -430,14 +446,19 @@ async_cancelled (GCancellable *cancellable,
G_IO_ERROR,
G_IO_ERROR_CANCELLED,
_("Operation was cancelled"));
+ G_LOCK (infos);
trigger_async_done (daemon, FALSE);
+ G_UNLOCK (infos);
}
static gboolean
async_timeout (gpointer data)
{
GDaemonFileEnumerator *daemon = G_DAEMON_FILE_ENUMERATOR (data);
+
+ G_LOCK (infos);
trigger_async_done (daemon, TRUE);
+ G_UNLOCK (infos);
return FALSE;
}