diff options
author | Alexander Larsson <alexl@redhat.com> | 2009-06-25 20:57:49 +0200 |
---|---|---|
committer | Alexander Larsson <alexl@redhat.com> | 2009-06-25 21:00:38 +0200 |
commit | 36d06a5fb2ff170fb2388d0afc82df954383b453 (patch) | |
tree | 9f62a3fb21b2454f17f19ef0a5743460be3784cd /client | |
parent | b548be8088f83b8cdeb66198e6092edfa518613d (diff) | |
download | gvfs-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.c | 25 |
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; } |