diff options
author | Philip Withnall <withnall@endlessm.com> | 2020-02-21 12:07:53 +0000 |
---|---|---|
committer | Philip Withnall <withnall@endlessm.com> | 2020-02-24 09:01:03 +0000 |
commit | af6dbece870a38dcce89ef55922ecfa085925823 (patch) | |
tree | 42974ad5808891c9e86a7b44109d0bf2c57c91e6 /gio | |
parent | ac274b400577131d7f52320d14077410b607b76e (diff) |
tests: Wait until unwatching the gdbus-testserver name has completed
Previously, the code in `ensure_gdbus_testserver_up()` created a proxy
object and watched its `name-owner` to see when the
`com.example.TestService` name appeared.
This ended up subscribing to three signals (one of them for name
ownership, and two unused for properties of the proxy), and was racy. In
particular, the `name-owner` property could be set before all D-Bus
messages had been processed — it could have been derived from getting
the owner of the name, for example.
This left unprocessed messages hanging around in the `context`, but that
context was never iterated again, which essentially leaked the
references held by those messages. That included a reference to the
`GDBusConnection`.
The first part of the fix is to simplify the code to use
`g_bus_watch_name_on_connection()`, so there’s only one signal
subscription to worry about.
The second part of the fix is to use the `GDestroyNotify` callback for
the watch data to be notified of when all D-Bus traffic has been
processed and the signal unsubscription is complete. At this point, it’s
guaranteed that there are no idle callbacks pending in the
`GMainContext`, since the `GDestroyNotify` callback is the last one
invoked on the `GMainContext`.
Essentially, this commit uses the `GDestroyNotify` callback as a
synchronisation message between the D-Bus worker thread and the thread
calling `ensure_gdbus_testserver_up()`.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Fixes: #1515
Diffstat (limited to 'gio')
-rw-r--r-- | gio/tests/gdbus-tests.c | 65 |
1 files changed, 44 insertions, 21 deletions
diff --git a/gio/tests/gdbus-tests.c b/gio/tests/gdbus-tests.c index 66f16e02f..bed0d24f7 100644 --- a/gio/tests/gdbus-tests.c +++ b/gio/tests/gdbus-tests.c @@ -86,26 +86,52 @@ _give_up (gpointer data) g_return_val_if_reached (TRUE); } +typedef struct +{ + GMainContext *context; + gboolean name_appeared; + gboolean unwatch_complete; +} WatchData; + +static void +name_appeared_cb (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + WatchData *data = user_data; + + g_assert (name_owner != NULL); + data->name_appeared = TRUE; + g_main_context_wakeup (data->context); +} + +static void +watch_free_cb (gpointer user_data) +{ + WatchData *data = user_data; + + data->unwatch_complete = TRUE; + g_main_context_wakeup (data->context); +} + void ensure_gdbus_testserver_up (GDBusConnection *connection, GMainContext *context) { - gchar *name_owner; GSource *timeout_source = NULL; - GDBusProxy *proxy; - GError *error = NULL; + guint watch_id; + WatchData data = { context, FALSE, FALSE }; g_main_context_push_thread_default (context); - proxy = g_dbus_proxy_new_sync (connection, - G_DBUS_PROXY_FLAGS_NONE, - NULL, /* GDBusInterfaceInfo */ - "com.example.TestService", /* name */ - "/com/example/TestObject", /* object path */ - "com.example.Frob", /* interface */ - NULL, /* GCancellable */ - &error); - g_assert_no_error (error); + watch_id = g_bus_watch_name_on_connection (connection, + "com.example.TestService", + G_BUS_NAME_WATCHER_FLAGS_NONE, + name_appeared_cb, + NULL, + &data, + watch_free_cb); timeout_source = g_timeout_source_new_seconds (60); g_source_set_callback (timeout_source, _give_up, @@ -113,20 +139,17 @@ ensure_gdbus_testserver_up (GDBusConnection *connection, NULL); g_source_attach (timeout_source, context); - while (TRUE) - { - name_owner = g_dbus_proxy_get_name_owner (proxy); + while (!data.name_appeared) + g_main_context_iteration (context, TRUE); - if (name_owner != NULL) - break; + g_bus_unwatch_name (watch_id); + watch_id = 0; - g_main_context_iteration (context, TRUE); - } + while (!data.unwatch_complete) + g_main_context_iteration (context, TRUE); g_source_destroy (timeout_source); g_source_unref (timeout_source); - g_free (name_owner); - g_object_unref (proxy); g_main_context_pop_thread_default (context); } |