[gvfs] Avoid deadlock on cancelling async enumerate_children



commit 36d06a5fb2ff170fb2388d0afc82df954383b453
Author: Alexander Larsson <alexl redhat com>
Date:   Thu Jun 25 20:57:49 2009 +0200

    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.

 client/gdaemonfileenumerator.c |   25 +++++++++++++++++++++++--
 1 files changed, 23 insertions(+), 2 deletions(-)
---
diff --git a/client/gdaemonfileenumerator.c b/client/gdaemonfileenumerator.c
index 7d5053a..1b6b084 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;
 }
 



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