From c53784092c2d69c4a1d916bb22235ffb8f0b5bad Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 17 Jan 2020 16:11:06 +0000 Subject: [PATCH 1/5] 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 Helps: #978 --- gio/gdbusconnection.c | 105 +++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 42 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 63f37ca4b..170057682 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -3229,18 +3229,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) { @@ -3251,10 +3242,37 @@ signal_data_free (SignalData *signal_data) 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, @@ -3440,7 +3458,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, { gchar *rule; SignalData *signal_data; - SignalSubscriber subscriber; + SignalSubscriber *subscriber; GPtrArray *signal_data_array; const gchar *sender_unique_name; @@ -3481,17 +3499,19 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, else sender_unique_name = ""; - 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 (); + 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; } @@ -3505,8 +3525,8 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, 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, @@ -3536,12 +3556,12 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, 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; } /* ---------------------------------------------------------------------------------------------------- */ @@ -3551,7 +3571,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, 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; @@ -3567,16 +3587,19 @@ unsubscribe_id_internal (GDBusConnection *connection, 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) { @@ -3634,13 +3657,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, @@ -3654,15 +3677,15 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, /* 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); } /* ---------------------------------------------------------------------------------------------------- */ @@ -3852,12 +3875,10 @@ schedule_callbacks (GDBusConnection *connection, 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; @@ -3930,7 +3951,7 @@ purge_all_signal_subscriptions (GDBusConnection *connection) GHashTableIter iter; gpointer key; GArray *ids; - GArray *subscribers; + GPtrArray *subscribers; guint n; ids = g_array_new (FALSE, FALSE, sizeof (guint)); @@ -3941,7 +3962,7 @@ purge_all_signal_subscriptions (GDBusConnection *connection) 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); @@ -3954,15 +3975,15 @@ purge_all_signal_subscriptions (GDBusConnection *connection) /* 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); } /* ---------------------------------------------------------------------------------------------------- */ -- 2.50.0 From d21d9fb02496f21864e4175d08b5f55416e832e6 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 17 Jan 2020 19:52:46 +0000 Subject: [PATCH 2/5] gdbusconnection: Tidy up destroy notification for signal subscriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tie the destruction of the `user_data` to the destruction of the `SignalSubscriber` struct. This is tidier, and ensures that the fields in `SignalSubscriber` are all immutable after being set, so the structure can safely be used across threads without locking. It doesn’t matter which thread we call `call_destroy_notify()` in, since it always defers calling `user_data_free_func` to the user-provided `GMainContext`. Signed-off-by: Philip Withnall Helps: #978 --- gio/gdbusconnection.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 170057682..2dba09fcc 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -3248,6 +3248,7 @@ signal_data_free (SignalData *signal_data) typedef struct { + /* All fields are immutable after construction. */ gatomicrefcount ref_count; GDBusSignalCallback callback; gpointer user_data; @@ -3268,6 +3269,14 @@ signal_subscriber_unref (SignalSubscriber *subscriber) { if (g_atomic_ref_count_dec (&subscriber->ref_count)) { + /* Destroy the user data. It doesn’t matter which thread + * signal_subscriber_unref() is called in (or whether it’s called with a + * lock held), as call_destroy_notify() always defers to the next + * #GMainContext iteration. */ + call_destroy_notify (subscriber->context, + subscriber->user_data_free_func, + subscriber->user_data); + g_main_context_unref (subscriber->context); g_free (subscriber); } @@ -3658,7 +3667,6 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, guint subscription_id) { GPtrArray *subscribers; - guint n; g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); g_return_if_fail (check_initialized (connection)); @@ -3674,17 +3682,6 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, /* invariant */ g_assert (subscribers->len == 0 || subscribers->len == 1); - /* call GDestroyNotify without lock held */ - for (n = 0; n < subscribers->len; n++) - { - SignalSubscriber *subscriber = subscribers->pdata[n]; - call_destroy_notify (subscriber->context, - subscriber->user_data_free_func, - subscriber->user_data); - subscriber->user_data_free_func = NULL; - subscriber->user_data = NULL; - } - g_ptr_array_unref (subscribers); } @@ -3972,17 +3969,6 @@ purge_all_signal_subscriptions (GDBusConnection *connection) } g_array_free (ids, TRUE); - /* call GDestroyNotify without lock held */ - for (n = 0; n < subscribers->len; n++) - { - SignalSubscriber *subscriber = subscribers->pdata[n]; - call_destroy_notify (subscriber->context, - subscriber->user_data_free_func, - subscriber->user_data); - subscriber->user_data_free_func = NULL; - subscriber->user_data = NULL; - } - g_ptr_array_unref (subscribers); } -- 2.50.0 From 5b7c9398a67ad274cb8faef8bf45461540a7198e Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 17 Jan 2020 19:38:55 +0000 Subject: [PATCH 3/5] gdbusconnection: Fix race when emitting D-Bus signal callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of storing a copy of the `callback` and `user_data` from a `SignalSubscriber` in a `SignalInstance` struct (which is the closure for signal callback data as it’s sent from the D-Bus worker thread to the thread which originally subscribed to a signal), store a strong reference to the `SignalSubscriber` struct itself. This keeps the `SignalSubscriber` alive until the emission is complete, which ensures that the `user_data` is not freed prematurely. It also slightly reduces the allocation size of `SignalInstance` (not that it matters). This is threadsafe because the fields in `SignalSubscriber` are all immutable after construction. Signed-off-by: Philip Withnall Helps: #978 --- gio/gdbusconnection.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 2dba09fcc..4cae53b29 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -3689,9 +3689,7 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, typedef struct { - guint subscription_id; - GDBusSignalCallback callback; - gpointer user_data; + SignalSubscriber *subscriber; /* (owned) */ GDBusMessage *message; GDBusConnection *connection; const gchar *sender; @@ -3723,7 +3721,7 @@ emit_signal_instance_in_idle_cb (gpointer data) #if 0 g_print ("in emit_signal_instance_in_idle_cb (id=%d sender=%s path=%s interface=%s member=%s params=%s)\n", - signal_instance->subscription_id, + signal_instance->subscriber->id, signal_instance->sender, signal_instance->path, signal_instance->interface, @@ -3735,18 +3733,18 @@ emit_signal_instance_in_idle_cb (gpointer data) CONNECTION_LOCK (signal_instance->connection); has_subscription = FALSE; if (g_hash_table_lookup (signal_instance->connection->map_id_to_signal_data, - GUINT_TO_POINTER (signal_instance->subscription_id)) != NULL) + GUINT_TO_POINTER (signal_instance->subscriber->id)) != NULL) has_subscription = TRUE; CONNECTION_UNLOCK (signal_instance->connection); if (has_subscription) - signal_instance->callback (signal_instance->connection, - signal_instance->sender, - signal_instance->path, - signal_instance->interface, - signal_instance->member, - parameters, - signal_instance->user_data); + signal_instance->subscriber->callback (signal_instance->connection, + signal_instance->sender, + signal_instance->path, + signal_instance->interface, + signal_instance->member, + parameters, + signal_instance->subscriber->user_data); g_variant_unref (parameters); @@ -3758,6 +3756,7 @@ signal_instance_free (SignalInstance *signal_instance) { g_object_unref (signal_instance->message); g_object_unref (signal_instance->connection); + signal_subscriber_unref (signal_instance->subscriber); g_free (signal_instance); } @@ -3877,9 +3876,7 @@ schedule_callbacks (GDBusConnection *connection, SignalInstance *signal_instance; signal_instance = g_new0 (SignalInstance, 1); - signal_instance->subscription_id = subscriber->id; - signal_instance->callback = subscriber->callback; - signal_instance->user_data = subscriber->user_data; + signal_instance->subscriber = signal_subscriber_ref (subscriber); signal_instance->message = g_object_ref (message); signal_instance->connection = g_object_ref (connection); signal_instance->sender = sender; -- 2.50.0 From a13bbdc1dc0cd4ec16c3e75d0cc1c07eb791b3bc Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 17 Jan 2020 19:56:58 +0000 Subject: [PATCH 4/5] gdbusconnection: Tidy up unsubscription code This just removes a now-redundant intermediate array. This means that the `SignalSubscriber` instances are now potentially freed a little sooner, inside the locked segment, but they are already careful to only call their `user_data_free_func` in the right thread. So that should not deadlock. Signed-off-by: Philip Withnall Helps: #978 --- gio/gdbusconnection.c | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 4cae53b29..4d99f8570 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -3576,15 +3576,16 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ /* called in any thread */ -/* must hold lock when calling this (except if connection->finalizing is TRUE) */ -static void +/* must hold lock when calling this (except if connection->finalizing is TRUE) + * returns the number of removed subscribers */ +static guint unsubscribe_id_internal (GDBusConnection *connection, - guint subscription_id, - GPtrArray *out_removed_subscribers) + guint subscription_id) { SignalData *signal_data; GPtrArray *signal_data_array; guint n; + guint n_removed = 0; signal_data = g_hash_table_lookup (connection->map_id_to_signal_data, GUINT_TO_POINTER (subscription_id)); @@ -3607,7 +3608,7 @@ unsubscribe_id_internal (GDBusConnection *connection, * guaranteed to be unique. */ g_warn_if_fail (g_hash_table_remove (connection->map_id_to_signal_data, GUINT_TO_POINTER (subscription_id))); - g_ptr_array_add (out_removed_subscribers, signal_subscriber_ref (subscriber)); + n_removed++; g_ptr_array_remove_index_fast (signal_data->subscribers, n); if (signal_data->subscribers->len == 0) @@ -3649,7 +3650,7 @@ unsubscribe_id_internal (GDBusConnection *connection, g_assert_not_reached (); out: - ; + return n_removed; } /** @@ -3666,23 +3667,17 @@ void g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, guint subscription_id) { - GPtrArray *subscribers; + guint n_subscribers_removed G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */; g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); g_return_if_fail (check_initialized (connection)); - subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref); - CONNECTION_LOCK (connection); - unsubscribe_id_internal (connection, - subscription_id, - subscribers); + n_subscribers_removed = unsubscribe_id_internal (connection, subscription_id); CONNECTION_UNLOCK (connection); /* invariant */ - g_assert (subscribers->len == 0 || subscribers->len == 1); - - g_ptr_array_unref (subscribers); + g_assert (n_subscribers_removed == 0 || n_subscribers_removed == 1); } /* ---------------------------------------------------------------------------------------------------- */ @@ -3945,7 +3940,6 @@ purge_all_signal_subscriptions (GDBusConnection *connection) GHashTableIter iter; gpointer key; GArray *ids; - GPtrArray *subscribers; guint n; ids = g_array_new (FALSE, FALSE, sizeof (guint)); @@ -3956,17 +3950,12 @@ purge_all_signal_subscriptions (GDBusConnection *connection) g_array_append_val (ids, subscription_id); } - 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); - unsubscribe_id_internal (connection, - subscription_id, - subscribers); + unsubscribe_id_internal (connection, subscription_id); } g_array_free (ids, TRUE); - - g_ptr_array_unref (subscribers); } /* ---------------------------------------------------------------------------------------------------- */ -- 2.50.0 From 0ec439aab1da5df011bed6a1a317439efacb6c25 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Fri, 17 Jan 2020 20:00:22 +0000 Subject: [PATCH 5/5] gdbusnameowning: Fix race between connection shutdown and NameLost MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As with all D-Bus signal subscriptions, it’s possible for a signal callback to be invoked in one thread (T1) while another thread (T2) is unsubscribing from that signal. In this case, T1 is the main thread, and T2 is the D-Bus connection worker thread which is unsubscribing all signals as it’s in the process of closing. Due to this possibility, all `user_data` for signal callbacks needs to be referenced outside the lifecycle of the code which subscribes/unsubscribes the signal. In other words, it’s not safe to subscribe to a signal, store the subscription ID in a struct, unsubscribe from the signal when freeing the struct, and dereference the struct in the signal callback. The data passed to the signal callback has to have its own strong reference. Instead, it’s safe to subscribe to a signal and add a strong reference to the struct, store the subscription ID in that struct, and unsubscribe from the signal when the last external reference to your struct is dropped. That unsubscription should break the refcount cycle between the signal connection and the struct, and allow the struct to be completely freed. Only with that approach is it safe to dereference the struct in the signal callback, if there’s any possibility that the signal might be unsubscribed from a separate thread. The tests need specific additional main loop cycles to completely emit the NameLost signal callback. Ideally they need refactoring, but this will do (1000 test cycles passed). Signed-off-by: Philip Withnall Fixes: #978 --- gio/gdbusnameowning.c | 15 ++++++++++----- gio/tests/gdbus-names.c | 11 ++++++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c index 2c2714db2..00554c16a 100644 --- a/gio/gdbusnameowning.c +++ b/gio/gdbusnameowning.c @@ -366,7 +366,12 @@ request_name_cb (GObject *source_object, connection = g_object_ref (client->connection); G_UNLOCK (lock); - /* start listening to NameLost and NameAcquired messages */ + /* Start listening to NameLost and NameAcquired messages. We hold + * references to the Client in the signal closures, since it’s possible + * for a signal to be in-flight after unsubscribing the signal handler. + * This creates a reference count cycle, but that’s explicitly broken by + * disconnecting the signal handlers before calling client_unref() in + * g_bus_unown_name(). */ if (connection != NULL) { client->name_lost_subscription_id = @@ -378,8 +383,8 @@ request_name_cb (GObject *source_object, client->name, G_DBUS_SIGNAL_FLAGS_NONE, on_name_lost_or_acquired, - client, - NULL); + client_ref (client), + (GDestroyNotify) client_unref); client->name_acquired_subscription_id = g_dbus_connection_signal_subscribe (connection, "org.freedesktop.DBus", @@ -389,8 +394,8 @@ request_name_cb (GObject *source_object, client->name, G_DBUS_SIGNAL_FLAGS_NONE, on_name_lost_or_acquired, - client, - NULL); + client_ref (client), + (GDestroyNotify) client_unref); g_object_unref (connection); } } diff --git a/gio/tests/gdbus-names.c b/gio/tests/gdbus-names.c index 648b54774..b42ba2141 100644 --- a/gio/tests/gdbus-names.c +++ b/gio/tests/gdbus-names.c @@ -189,6 +189,7 @@ test_bus_own_name (void) * Stop owning the name - this should invoke our free func */ g_bus_unown_name (id); + g_main_loop_run (loop); g_assert_cmpint (data.num_free_func, ==, 2); /* @@ -330,6 +331,7 @@ test_bus_own_name (void) g_assert_cmpint (data2.num_acquired, ==, 0); g_assert_cmpint (data2.num_lost, ==, 1); g_bus_unown_name (id2); + g_main_loop_run (loop); g_assert_cmpint (data2.num_bus_acquired, ==, 0); g_assert_cmpint (data2.num_acquired, ==, 0); g_assert_cmpint (data2.num_lost, ==, 1); @@ -355,6 +357,7 @@ test_bus_own_name (void) g_assert_cmpint (data2.num_acquired, ==, 0); g_assert_cmpint (data2.num_lost, ==, 1); g_bus_unown_name (id2); + g_main_loop_run (loop); g_assert_cmpint (data2.num_bus_acquired, ==, 0); g_assert_cmpint (data2.num_acquired, ==, 0); g_assert_cmpint (data2.num_lost, ==, 1); @@ -365,6 +368,7 @@ test_bus_own_name (void) */ data.expect_null_connection = FALSE; g_bus_unown_name (id); + g_main_loop_run (loop); g_assert_cmpint (data.num_bus_acquired, ==, 1); g_assert_cmpint (data.num_acquired, ==, 1); g_assert_cmpint (data.num_free_func, ==, 4); @@ -418,6 +422,7 @@ test_bus_own_name (void) g_assert_cmpint (data2.num_acquired, ==, 0); g_assert_cmpint (data2.num_lost, ==, 1); g_bus_unown_name (id2); + g_main_loop_run (loop); g_assert_cmpint (data2.num_bus_acquired, ==, 0); g_assert_cmpint (data2.num_acquired, ==, 0); g_assert_cmpint (data2.num_lost, ==, 1); @@ -450,8 +455,9 @@ test_bus_own_name (void) g_assert_cmpint (data2.num_bus_acquired, ==, 0); /* ok, make owner2 release the name - then wait for owner to automagically reacquire it */ g_bus_unown_name (id2); - g_assert_cmpint (data2.num_free_func, ==, 1); g_main_loop_run (loop); + g_main_loop_run (loop); + g_assert_cmpint (data2.num_free_func, ==, 1); g_assert_cmpint (data.num_acquired, ==, 2); g_assert_cmpint (data.num_lost, ==, 1); @@ -466,6 +472,7 @@ test_bus_own_name (void) g_assert_cmpint (data.num_acquired, ==, 2); g_assert_cmpint (data.num_lost, ==, 2); g_bus_unown_name (id); + g_main_loop_run (loop); g_assert_cmpint (data.num_free_func, ==, 5); g_object_unref (c); @@ -648,6 +655,7 @@ test_bus_watch_name (void) /* unown the name */ g_bus_unown_name (owner_id); + g_main_loop_run (loop); g_assert_cmpint (data.num_acquired, ==, 1); g_assert_cmpint (data.num_free_func, ==, 2); @@ -707,6 +715,7 @@ test_bus_watch_name (void) g_assert_cmpint (data.num_free_func, ==, 1); g_bus_unown_name (owner_id); + g_main_loop_run (loop); g_assert_cmpint (data.num_free_func, ==, 2); session_bus_down (); -- 2.50.0