Fix a possible deadlock
authorMatthias Clasen <mclasen@redhat.com>
Tue, 3 Aug 2010 14:41:21 +0000 (10:41 -0400)
committerMatthias Clasen <mclasen@redhat.com>
Tue, 3 Aug 2010 14:41:21 +0000 (10:41 -0400)
the FdSource was calling g_cancellable_disconnect while holding the
main context lock, which is bad news if the ::cancelled handler is
trying to get that lock to wake up the mainloop...

Bug 586432

gio/gasynchelper.c
gio/tests/unix-streams.c

index b439703..8e2e8e6 100644 (file)
@@ -86,9 +86,13 @@ fd_source_finalize (GSource *source)
 {
   FDSource *fd_source = (FDSource *)source;
 
+  /* we don't use g_cancellable_disconnect() here, since we are holding
+   * the main context lock here, and the ::disconnect signal handler
+   * will try to grab that, and deadlock looms.
+   */
   if (fd_source->cancelled_tag)
-    g_cancellable_disconnect (fd_source->cancellable,
-                             fd_source->cancelled_tag);
+    g_signal_handler_disconnect (fd_source->cancellable,
+                                 fd_source->cancelled_tag);
 
   if (fd_source->cancellable)
     g_object_unref (fd_source->cancellable);
index ea34017..2d06b8e 100644 (file)
@@ -33,16 +33,6 @@ int writer_pipe[2], reader_pipe[2];
 GCancellable *writer_cancel, *reader_cancel, *main_cancel;
 GMainLoop *loop;
 
-static gboolean
-cancel_main (gpointer data)
-{
-  GCancellable *main_cancel = data;
-
-  g_cancellable_cancel (main_cancel);
-
-  return FALSE;
-}
-
 
 static gpointer
 writer_thread (gpointer user_data)
@@ -74,17 +64,7 @@ writer_thread (gpointer user_data)
 
   if (g_cancellable_is_cancelled (writer_cancel))
     {
-      /* FIXME: directly calling g_cancellable_cancel (main_cancel) here
-       * leads to sporadic deadlock, because it will try to wake up the
-       * main context, for which it needs to acquire the main context lock.
-       * This lock may be held by the main loop running in the main thread,
-       * and it may be held while the main thread is blocking in
-       * fd_source_finalize -> g_cancellable_disconnect
-       * until the ::cancelled callbacks have run.
-       *
-       * Work around by deferring the cancellation to a timeout.
-       */
-      g_timeout_add (0, cancel_main, main_cancel);
+      g_cancellable_cancel (main_cancel);
       g_object_unref (out);
       return NULL;
     }