mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-07-31 22:23:39 +02:00
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
This commit is contained in:
@@ -86,26 +86,52 @@ _give_up (gpointer data)
|
|||||||
g_return_val_if_reached (TRUE);
|
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
|
void
|
||||||
ensure_gdbus_testserver_up (GDBusConnection *connection,
|
ensure_gdbus_testserver_up (GDBusConnection *connection,
|
||||||
GMainContext *context)
|
GMainContext *context)
|
||||||
{
|
{
|
||||||
gchar *name_owner;
|
|
||||||
GSource *timeout_source = NULL;
|
GSource *timeout_source = NULL;
|
||||||
GDBusProxy *proxy;
|
guint watch_id;
|
||||||
GError *error = NULL;
|
WatchData data = { context, FALSE, FALSE };
|
||||||
|
|
||||||
g_main_context_push_thread_default (context);
|
g_main_context_push_thread_default (context);
|
||||||
|
|
||||||
proxy = g_dbus_proxy_new_sync (connection,
|
watch_id = g_bus_watch_name_on_connection (connection,
|
||||||
G_DBUS_PROXY_FLAGS_NONE,
|
"com.example.TestService",
|
||||||
NULL, /* GDBusInterfaceInfo */
|
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||||
"com.example.TestService", /* name */
|
name_appeared_cb,
|
||||||
"/com/example/TestObject", /* object path */
|
NULL,
|
||||||
"com.example.Frob", /* interface */
|
&data,
|
||||||
NULL, /* GCancellable */
|
watch_free_cb);
|
||||||
&error);
|
|
||||||
g_assert_no_error (error);
|
|
||||||
|
|
||||||
timeout_source = g_timeout_source_new_seconds (60);
|
timeout_source = g_timeout_source_new_seconds (60);
|
||||||
g_source_set_callback (timeout_source, _give_up,
|
g_source_set_callback (timeout_source, _give_up,
|
||||||
@@ -113,20 +139,17 @@ ensure_gdbus_testserver_up (GDBusConnection *connection,
|
|||||||
NULL);
|
NULL);
|
||||||
g_source_attach (timeout_source, context);
|
g_source_attach (timeout_source, context);
|
||||||
|
|
||||||
while (TRUE)
|
while (!data.name_appeared)
|
||||||
{
|
g_main_context_iteration (context, TRUE);
|
||||||
name_owner = g_dbus_proxy_get_name_owner (proxy);
|
|
||||||
|
|
||||||
if (name_owner != NULL)
|
g_bus_unwatch_name (watch_id);
|
||||||
break;
|
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_destroy (timeout_source);
|
||||||
g_source_unref (timeout_source);
|
g_source_unref (timeout_source);
|
||||||
g_free (name_owner);
|
|
||||||
g_object_unref (proxy);
|
|
||||||
|
|
||||||
g_main_context_pop_thread_default (context);
|
g_main_context_pop_thread_default (context);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user