We have some news to share for the request index beta feature. We’ve added more options to sort your requests, counters to the individual filters and documentation for the search functionality. Checkout the blog post for more details.

File glib2-allocate-SignalSubscriber-structs-individually.patch of Package glib2.42479

From 9b1c8d7dd52c69962e58978f58a82bd703280735 Mon Sep 17 00:00:00 2001
From: Philip Withnall <withnall@endlessm.com>
Date: Fri, 17 Jan 2020 16:11:06 +0000
Subject: [PATCH] gdbusconnection: Allocate SignalSubscriber structs
 individually

The `SignalSubscriber` structs contain the callback and `user_data` of each
subscriber to a signal, along with the `guint id` token held by that
subscriber to identify their subscription. There are one or more
`SignalSubscriber` structs for a given signal match rule, which is
represented as a `SignalData` struct.

Previously, the `SignalSubscriber` structs were stored in a `GArray` in
the `SignalData` struct, to reduce the number of allocations needed
when subscribing to a signal.

However, this means that a `SignalSubscriber` struct cannot have a
lifetime which exceeds the `SignalData` which contains it. In order to
fix the race in #978, one thread needs to be able to unsubscribe from a
signal (destroying the `SignalData` struct) while zero or more other
threads are in the process of calling the callbacks from a previous
emission of that signal (using the callback and `user_data` from zero or
more `SignalSubscriber` structs). Multiple threads could be calling
callbacks because callbacks are invoked in the `GMainContext` which
originally made a subscription, and GDBus supports subscribing to a
signal from multiple threads. In that case, the callbacks are dispatched
to multiple threads.

In order to allow the `SignalSubscriber` structs to outlive the
`SignalData` which contained their old match rule, store them in a
`GPtrArray` in the `SignalData` struct, and refcount them individually.

This commit in itself should make no functional changes to how GDBus
works, but will allow following commits to do so.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Helps: #978
---
 gio/gdbusconnection.c | 105 +++++++++++++++++++++++++-----------------
 1 file changed, 63 insertions(+), 42 deletions(-)

diff -urp glib-2.62.6.orig/gio/gdbusconnection.c glib-2.62.6/gio/gdbusconnection.c
--- glib-2.62.6.orig/gio/gdbusconnection.c	2024-05-13 16:46:01.622711826 -0500
+++ glib-2.62.6/gio/gdbusconnection.c	2024-05-15 14:29:05.204820452 -0500
@@ -3249,18 +3249,9 @@ typedef struct
   gchar *object_path;
   gchar *arg0;
   GDBusSignalFlags flags;
-  GArray *subscribers;
+  GPtrArray *subscribers;  /* (owned) (element-type SignalSubscriber) */
 } SignalData;
 
-typedef struct
-{
-  GDBusSignalCallback callback;
-  gpointer user_data;
-  GDestroyNotify user_data_free_func;
-  guint id;
-  GMainContext *context;
-} SignalSubscriber;
-
 static void
 signal_data_free (SignalData *signal_data)
 {
@@ -3271,10 +3262,37 @@ signal_data_free (SignalData *signal_dat
   g_free (signal_data->member);
   g_free (signal_data->object_path);
   g_free (signal_data->arg0);
-  g_array_free (signal_data->subscribers, TRUE);
+  g_ptr_array_unref (signal_data->subscribers);
   g_free (signal_data);
 }
 
+typedef struct
+{
+  gatomicrefcount ref_count;
+  GDBusSignalCallback callback;
+  gpointer user_data;
+  GDestroyNotify user_data_free_func;
+  guint id;
+  GMainContext *context;
+} SignalSubscriber;
+
+static SignalSubscriber *
+signal_subscriber_ref (SignalSubscriber *subscriber)
+{
+  g_atomic_ref_count_inc (&subscriber->ref_count);
+  return subscriber;
+}
+
+static void
+signal_subscriber_unref (SignalSubscriber *subscriber)
+{
+  if (g_atomic_ref_count_dec (&subscriber->ref_count))
+    {
+      g_main_context_unref (subscriber->context);
+      g_free (subscriber);
+    }
+}
+
 static gchar *
 args_to_rule (const gchar      *sender,
               const gchar      *interface_name,
@@ -3465,7 +3483,7 @@ g_dbus_connection_signal_subscribe (GDBu
 {
   gchar *rule;
   SignalData *signal_data;
-  SignalSubscriber subscriber;
+  SignalSubscriber *subscriber;
   GPtrArray *signal_data_array;
   const gchar *sender_unique_name;
 
@@ -3506,17 +3524,19 @@ g_dbus_connection_signal_subscribe (GDBu
   else
     sender_unique_name = "";
 
-  subscriber.callback = callback;
-  subscriber.user_data = user_data;
-  subscriber.user_data_free_func = user_data_free_func;
-  subscriber.id = g_atomic_int_add (&_global_subscriber_id, 1); /* TODO: overflow etc. */
-  subscriber.context = g_main_context_ref_thread_default ();
+  subscriber = g_new0 (SignalSubscriber, 1);
+  subscriber->ref_count = 1;
+  subscriber->callback = callback;
+  subscriber->user_data = user_data;
+  subscriber->user_data_free_func = user_data_free_func;
+  subscriber->id = (guint) g_atomic_int_add (&_global_subscriber_id, 1); /* TODO: overflow etc. */
+  subscriber->context = g_main_context_ref_thread_default ();
 
   /* see if we've already have this rule */
   signal_data = g_hash_table_lookup (connection->map_rule_to_signal_data, rule);
   if (signal_data != NULL)
     {
-      g_array_append_val (signal_data->subscribers, subscriber);
+      g_ptr_array_add (signal_data->subscribers, subscriber);
       g_free (rule);
       goto out;
     }
@@ -3530,8 +3550,8 @@ g_dbus_connection_signal_subscribe (GDBu
   signal_data->object_path           = g_strdup (object_path);
   signal_data->arg0                  = g_strdup (arg0);
   signal_data->flags                 = flags;
-  signal_data->subscribers           = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber));
-  g_array_append_val (signal_data->subscribers, subscriber);
+  signal_data->subscribers           = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref);
+  g_ptr_array_add (signal_data->subscribers, subscriber);
 
   g_hash_table_insert (connection->map_rule_to_signal_data,
                        signal_data->rule,
@@ -3561,12 +3581,12 @@ g_dbus_connection_signal_subscribe (GDBu
 
  out:
   g_hash_table_insert (connection->map_id_to_signal_data,
-                       GUINT_TO_POINTER (subscriber.id),
+                       GUINT_TO_POINTER (subscriber->id),
                        signal_data);
 
   CONNECTION_UNLOCK (connection);
 
-  return subscriber.id;
+  return subscriber->id;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -3576,7 +3596,7 @@ g_dbus_connection_signal_subscribe (GDBu
 static void
 unsubscribe_id_internal (GDBusConnection *connection,
                          guint            subscription_id,
-                         GArray          *out_removed_subscribers)
+                         GPtrArray       *out_removed_subscribers)
 {
   SignalData *signal_data;
   GPtrArray *signal_data_array;
@@ -3592,16 +3612,19 @@ unsubscribe_id_internal (GDBusConnection
 
   for (n = 0; n < signal_data->subscribers->len; n++)
     {
-      SignalSubscriber *subscriber;
+      SignalSubscriber *subscriber = signal_data->subscribers->pdata[n];
 
-      subscriber = &(g_array_index (signal_data->subscribers, SignalSubscriber, n));
       if (subscriber->id != subscription_id)
         continue;
 
+      /* It’s OK to rearrange the array order using the ‘fast’ #GPtrArray
+       * removal functions, since we’re going to exit the loop below anyway — we
+       * never move on to the next element. Secondly, subscription IDs are
+       * guaranteed to be unique. */
       g_warn_if_fail (g_hash_table_remove (connection->map_id_to_signal_data,
                                            GUINT_TO_POINTER (subscription_id)));
-      g_array_append_val (out_removed_subscribers, *subscriber);
-      g_array_remove_index (signal_data->subscribers, n);
+      g_ptr_array_add (out_removed_subscribers, signal_subscriber_ref (subscriber));
+      g_ptr_array_remove_index_fast (signal_data->subscribers, n);
 
       if (signal_data->subscribers->len == 0)
         {
@@ -3659,13 +3682,13 @@ void
 g_dbus_connection_signal_unsubscribe (GDBusConnection *connection,
                                       guint            subscription_id)
 {
-  GArray *subscribers;
+  GPtrArray *subscribers;
   guint n;
 
   g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
   g_return_if_fail (check_initialized (connection));
 
-  subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber));
+  subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref);
 
   CONNECTION_LOCK (connection);
   unsubscribe_id_internal (connection,
@@ -3679,15 +3702,15 @@ g_dbus_connection_signal_unsubscribe (GD
   /* call GDestroyNotify without lock held */
   for (n = 0; n < subscribers->len; n++)
     {
-      SignalSubscriber *subscriber;
-      subscriber = &(g_array_index (subscribers, SignalSubscriber, n));
+      SignalSubscriber *subscriber = subscribers->pdata[n];
       call_destroy_notify (subscriber->context,
                            subscriber->user_data_free_func,
                            subscriber->user_data);
-      g_main_context_unref (subscriber->context);
+      subscriber->user_data_free_func = NULL;
+      subscriber->user_data = NULL;
     }
 
-  g_array_free (subscribers, TRUE);
+  g_ptr_array_unref (subscribers);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -3877,12 +3900,10 @@ schedule_callbacks (GDBusConnection *con
 
       for (m = 0; m < signal_data->subscribers->len; m++)
         {
-          SignalSubscriber *subscriber;
+          SignalSubscriber *subscriber = signal_data->subscribers->pdata[m];
           GSource *idle_source;
           SignalInstance *signal_instance;
 
-          subscriber = &(g_array_index (signal_data->subscribers, SignalSubscriber, m));
-
           signal_instance = g_new0 (SignalInstance, 1);
           signal_instance->subscription_id = subscriber->id;
           signal_instance->callback = subscriber->callback;
@@ -3955,7 +3976,7 @@ purge_all_signal_subscriptions (GDBusCon
   GHashTableIter iter;
   gpointer key;
   GArray *ids;
-  GArray *subscribers;
+  GPtrArray *subscribers;
   guint n;
 
   ids = g_array_new (FALSE, FALSE, sizeof (guint));
@@ -3966,7 +3987,7 @@ purge_all_signal_subscriptions (GDBusCon
       g_array_append_val (ids, subscription_id);
     }
 
-  subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber));
+  subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref);
   for (n = 0; n < ids->len; n++)
     {
       guint subscription_id = g_array_index (ids, guint, n);
@@ -3979,15 +4000,15 @@ purge_all_signal_subscriptions (GDBusCon
   /* call GDestroyNotify without lock held */
   for (n = 0; n < subscribers->len; n++)
     {
-      SignalSubscriber *subscriber;
-      subscriber = &(g_array_index (subscribers, SignalSubscriber, n));
+      SignalSubscriber *subscriber = subscribers->pdata[n];
       call_destroy_notify (subscriber->context,
                            subscriber->user_data_free_func,
                            subscriber->user_data);
-      g_main_context_unref (subscriber->context);
+      subscriber->user_data_free_func = NULL;
+      subscriber->user_data = NULL;
     }
 
-  g_array_free (subscribers, TRUE);
+  g_ptr_array_unref (subscribers);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
Only in glib-2.62.6/gio: gdbusconnection.c.orig
Only in glib-2.62.6/gio: gdbusconnection.c.rej
openSUSE Build Service is sponsored by