[glib/wip/pwithnall/gdbus-names-livelock] gdbusnamewatching: Ensure GDestroyNotify is called in correct context



commit 72f692eae4954da8e0ad0651bb87984f2ed82cb1
Author: Philip Withnall <pwithnall endlessos org>
Date:   Mon Jun 21 14:16:36 2021 +0100

    gdbusnamewatching: Ensure GDestroyNotify is called in correct context
    
    The documentation for `g_bus_watch_name()` implies that the
    `GDestroyNotify` for the user data will be called in the current thread
    default `GMainContext`. Currently, it could be called in any thread, as
    `client_unref()` can be called in any thread.
    
    Fix that by deferring it to an idle source if `client_unref()` finalises
    the `Client` object in a different thread.
    
    Signed-off-by: Philip Withnall <pwithnall endlessos org>

 gio/gdbusnamewatching.c | 28 ++++++++++++++++++++++++++--
 1 file changed, 26 insertions(+), 2 deletions(-)
---
diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c
index 40aee5a3c..3688eb066 100644
--- a/gio/gdbusnamewatching.c
+++ b/gio/gdbusnamewatching.c
@@ -90,6 +90,13 @@ client_ref (Client *client)
   return client;
 }
 
+static gboolean
+free_user_data_cb (gpointer user_data)
+{
+  /* The user data is actually freed by the GDestroyNotify for the idle source */
+  return G_SOURCE_REMOVE;
+}
+
 static void
 client_unref (Client *client)
 {
@@ -105,9 +112,26 @@ client_unref (Client *client)
         }
       g_free (client->name);
       g_free (client->name_owner);
-      g_main_context_unref (client->main_context);
+
       if (client->user_data_free_func != NULL)
-        client->user_data_free_func (client->user_data);
+        {
+          /* Ensure client->user_data_free_func() is called from the right thread */
+          if (client->main_context != g_main_context_get_thread_default ())
+            {
+              GSource *idle_source = g_idle_source_new ();
+              g_source_set_callback (idle_source, free_user_data_cb,
+                                     client->user_data,
+                                     client->user_data_free_func);
+              g_source_set_name (idle_source, "[gio, gdbusnamewatching.c] free_user_data_cb");
+              g_source_attach (idle_source, client->main_context);
+              g_source_unref (idle_source);
+            }
+          else
+            client->user_data_free_func (client->user_data);
+        }
+
+      g_main_context_unref (client->main_context);
+
       g_free (client);
     }
 }


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