summaryrefslogtreecommitdiff
path: root/gio/tests/gdbus-names.c
diff options
context:
space:
mode:
Diffstat (limited to 'gio/tests/gdbus-names.c')
-rw-r--r--gio/tests/gdbus-names.c335
1 files changed, 303 insertions, 32 deletions
diff --git a/gio/tests/gdbus-names.c b/gio/tests/gdbus-names.c
index 05d2015d0..8504220a9 100644
--- a/gio/tests/gdbus-names.c
+++ b/gio/tests/gdbus-names.c
@@ -1,6 +1,7 @@
/* GLib testing framework examples and tests
*
* Copyright (C) 2008-2010 Red Hat, Inc.
+ * Copyright (C) 2021 Frederic Martinsons
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -16,6 +17,7 @@
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: David Zeuthen <davidz@redhat.com>
+ * Author: Frederic Martinsons <frederic.martinsons@gmail.com>
*/
#include <gio/gio.h>
@@ -495,6 +497,20 @@ typedef struct
guint num_free_func;
} WatchNameData;
+typedef struct
+{
+ WatchNameData data;
+ GDBusConnection *connection;
+ GMutex cond_mutex;
+ GCond cond;
+ gboolean started;
+ gboolean name_acquired;
+ gboolean ended;
+ gboolean unwatch_early;
+ GMutex mutex;
+ guint watch_id;
+} WatchNameThreadData;
+
static void
watch_name_data_free_func (WatchNameData *data)
{
@@ -514,7 +530,7 @@ w_name_acquired_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
- WatchNameData *data = user_data;
+ OwnNameData *data = user_data;
data->num_acquired += 1;
g_main_loop_quit (loop);
}
@@ -524,7 +540,7 @@ w_name_lost_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
- WatchNameData *data = user_data;
+ OwnNameData *data = user_data;
data->num_lost += 1;
g_main_loop_quit (loop);
}
@@ -613,6 +629,7 @@ stop_service (GDBusConnection *connection,
{
GError *error = NULL;
GDBusProxy *proxy = NULL;
+ GVariant *result = NULL;
data->num_vanished = 0;
@@ -626,15 +643,17 @@ stop_service (GDBusConnection *connection,
&error);
g_assert_no_error (error);
- g_dbus_proxy_call_sync (proxy,
- "Quit",
- NULL,
- G_DBUS_CALL_FLAGS_NO_AUTO_START,
- 100,
- NULL,
- &error);
+ result = g_dbus_proxy_call_sync (proxy,
+ "Quit",
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 100,
+ NULL,
+ &error);
g_assert_no_error (error);
g_object_unref (proxy);
+ if (result)
+ g_variant_unref (result);
while (data->num_vanished == 0)
g_main_loop_run (loop);
}
@@ -643,6 +662,7 @@ static void
test_bus_watch_name (gconstpointer d)
{
WatchNameData data;
+ OwnNameData own_data;
guint id;
guint owner_id;
GDBusConnection *connection;
@@ -685,15 +705,16 @@ test_bus_watch_name (gconstpointer d)
g_assert_cmpint (data.num_appeared, ==, 0);
g_assert_cmpint (data.num_vanished, ==, 1);
g_assert_cmpint (data.num_free_func, ==, 1);
+ data.num_free_func = 0;
/*
* Now bring up a bus, own a name, and then start watching it.
*/
session_bus_up ();
/* own the name */
- data.num_free_func = 0;
- data.num_acquired = 0;
- data.num_lost = 0;
+ own_data.num_free_func = 0;
+ own_data.num_acquired = 0;
+ own_data.num_lost = 0;
data.expect_null_connection = FALSE;
owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
name,
@@ -701,11 +722,11 @@ test_bus_watch_name (gconstpointer d)
w_bus_acquired_handler,
w_name_acquired_handler,
w_name_lost_handler,
- &data,
- (GDestroyNotify) watch_name_data_free_func);
+ &own_data,
+ (GDestroyNotify) own_name_data_free_func);
g_main_loop_run (loop);
- g_assert_cmpint (data.num_acquired, ==, 1);
- g_assert_cmpint (data.num_lost, ==, 0);
+ g_assert_cmpint (own_data.num_acquired, ==, 1);
+ g_assert_cmpint (own_data.num_lost, ==, 0);
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert (connection != NULL);
@@ -750,9 +771,9 @@ test_bus_watch_name (gconstpointer d)
/* 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);
-
+ g_assert_cmpint (own_data.num_acquired, ==, 1);
+ g_assert_cmpint (own_data.num_free_func, ==, 1);
+ own_data.num_free_func = 0;
/*
* Create a watcher and then make a name be owned.
*
@@ -802,21 +823,21 @@ test_bus_watch_name (gconstpointer d)
if (!watch_name_test->existing_service)
{
/* own the name */
- data.num_acquired = 0;
- data.num_lost = 0;
- data.expect_null_connection = FALSE;
+ own_data.num_acquired = 0;
+ own_data.num_lost = 0;
+ own_data.expect_null_connection = FALSE;
owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
name,
G_BUS_NAME_OWNER_FLAGS_NONE,
w_bus_acquired_handler,
w_name_acquired_handler,
w_name_lost_handler,
- &data,
- (GDestroyNotify) watch_name_data_free_func);
- while (data.num_acquired == 0 || data.num_appeared == 0)
+ &own_data,
+ (GDestroyNotify) own_name_data_free_func);
+ while (own_data.num_acquired == 0 || data.num_appeared == 0)
g_main_loop_run (loop);
- g_assert_cmpint (data.num_acquired, ==, 1);
- g_assert_cmpint (data.num_lost, ==, 0);
+ g_assert_cmpint (own_data.num_acquired, ==, 1);
+ g_assert_cmpint (own_data.num_lost, ==, 0);
g_assert_cmpint (data.num_appeared, ==, 1);
g_assert_cmpint (data.num_vanished, ==, 1);
}
@@ -835,12 +856,12 @@ test_bus_watch_name (gconstpointer d)
if (!watch_name_test->existing_service)
{
g_main_loop_run (loop);
- g_assert_cmpint (data.num_lost, ==, 1);
+ g_assert_cmpint (own_data.num_lost, ==, 1);
g_assert_cmpint (data.num_vanished, ==, 2);
}
else
{
- g_assert_cmpint (data.num_lost, ==, 0);
+ g_assert_cmpint (own_data.num_lost, ==, 0);
g_assert_cmpint (data.num_vanished, ==, 1);
}
g_bus_unwatch_name (id);
@@ -850,11 +871,253 @@ test_bus_watch_name (gconstpointer d)
{
g_bus_unown_name (owner_id);
g_main_loop_run (loop);
- g_assert_cmpint (data.num_free_func, ==, 2);
+ g_assert_cmpint (own_data.num_free_func, ==, 1);
+ }
+ session_bus_down ();
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* Called in the same thread as watcher_thread() */
+static void
+t_watch_name_data_free_func (WatchNameThreadData *thread_data)
+{
+ thread_data->data.num_free_func++;
+}
+
+/* Called in the same thread as watcher_thread() */
+static void
+t_name_appeared_handler (GDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ gpointer user_data)
+{
+ WatchNameThreadData *thread_data = user_data;
+ thread_data->data.num_appeared += 1;
+}
+
+/* Called in the same thread as watcher_thread() */
+static void
+t_name_vanished_handler (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ WatchNameThreadData *thread_data = user_data;
+ thread_data->data.num_vanished += 1;
+}
+
+/* Called in the thread which constructed the GDBusConnection */
+static void
+connection_closed_cb (GDBusConnection *connection,
+ gboolean remote_peer_vanished,
+ GError *error,
+ gpointer user_data)
+{
+ WatchNameThreadData *thread_data = (WatchNameThreadData *) user_data;
+ if (thread_data->unwatch_early)
+ {
+ g_mutex_lock (&thread_data->mutex);
+ g_bus_unwatch_name (g_atomic_int_get (&thread_data->watch_id));
+ g_atomic_int_set (&thread_data->watch_id, 0);
+ g_cond_signal (&thread_data->cond);
+ g_mutex_unlock (&thread_data->mutex);
}
+}
+
+static gpointer
+watcher_thread (gpointer user_data)
+{
+ WatchNameThreadData *thread_data = user_data;
+ GMainContext *thread_context;
+
+ thread_context = g_main_context_new ();
+ g_main_context_push_thread_default (thread_context);
+
+ // Notify that the thread has started
+ g_mutex_lock (&thread_data->cond_mutex);
+ g_atomic_int_set (&thread_data->started, TRUE);
+ g_cond_signal (&thread_data->cond);
+ g_mutex_unlock (&thread_data->cond_mutex);
+
+ // Wait for the main thread to own the name before watching it
+ g_mutex_lock (&thread_data->cond_mutex);
+ while (!g_atomic_int_get (&thread_data->name_acquired))
+ g_cond_wait (&thread_data->cond, &thread_data->cond_mutex);
+ g_mutex_unlock (&thread_data->cond_mutex);
+
+ thread_data->data.num_appeared = 0;
+ thread_data->data.num_vanished = 0;
+ thread_data->data.num_free_func = 0;
+ // g_signal_connect_after is important to have default handler be called before our code
+ g_signal_connect_after (thread_data->connection, "closed", G_CALLBACK (connection_closed_cb), thread_data);
+
+ g_mutex_lock (&thread_data->mutex);
+ thread_data->watch_id = g_bus_watch_name_on_connection (thread_data->connection,
+ "org.gtk.GDBus.Name1",
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ t_name_appeared_handler,
+ t_name_vanished_handler,
+ thread_data,
+ (GDestroyNotify) t_watch_name_data_free_func);
+ g_mutex_unlock (&thread_data->mutex);
+
+ g_assert_cmpint (thread_data->data.num_appeared, ==, 0);
+ g_assert_cmpint (thread_data->data.num_vanished, ==, 0);
+ while (thread_data->data.num_appeared == 0)
+ g_main_context_iteration (thread_context, TRUE);
+ g_assert_cmpint (thread_data->data.num_appeared, ==, 1);
+ g_assert_cmpint (thread_data->data.num_vanished, ==, 0);
+ thread_data->data.num_appeared = 0;
+
+ /* Close the connection and:
+ * - check that we had received a vanished event even begin in different thread
+ * - or check that unwatching the bus when a vanished had been scheduled
+ * make it correctly unscheduled (unwatch_early condition)
+ */
+ g_dbus_connection_close_sync (thread_data->connection, NULL, NULL);
+ if (thread_data->unwatch_early)
+ {
+ // Wait for the main thread to iterate in order to have close connection handled
+ g_mutex_lock (&thread_data->mutex);
+ while (g_atomic_int_get (&thread_data->watch_id) != 0)
+ g_cond_wait (&thread_data->cond, &thread_data->mutex);
+ g_mutex_unlock (&thread_data->mutex);
+
+ while (thread_data->data.num_free_func == 0)
+ g_main_context_iteration (thread_context, TRUE);
+ g_assert_cmpint (thread_data->data.num_vanished, ==, 0);
+ g_assert_cmpint (thread_data->data.num_appeared, ==, 0);
+ g_assert_cmpint (thread_data->data.num_free_func, ==, 1);
+ }
+ else
+ {
+ while (thread_data->data.num_vanished == 0)
+ {
+ /*
+ * Close of connection is treated in the context of the thread which
+ * creates the connection. We must run iteration on it (to have the 'closed'
+ * signal handled) and also run current thread loop to have name_vanished
+ * callback handled.
+ */
+ g_main_context_iteration (thread_context, TRUE);
+ }
+ g_assert_cmpint (thread_data->data.num_vanished, ==, 1);
+ g_assert_cmpint (thread_data->data.num_appeared, ==, 0);
+ g_mutex_lock (&thread_data->mutex);
+ g_bus_unwatch_name (g_atomic_int_get (&thread_data->watch_id));
+ g_atomic_int_set (&thread_data->watch_id, 0);
+ g_mutex_unlock (&thread_data->mutex);
+ while (thread_data->data.num_free_func == 0)
+ g_main_context_iteration (thread_context, TRUE);
+ g_assert_cmpint (thread_data->data.num_free_func, ==, 1);
+ }
+
+ g_mutex_lock (&thread_data->cond_mutex);
+ thread_data->ended = TRUE;
+ g_main_context_wakeup (NULL);
+ g_cond_signal (&thread_data->cond);
+ g_mutex_unlock (&thread_data->cond_mutex);
+
+ g_signal_handlers_disconnect_by_func (thread_data->connection, connection_closed_cb, thread_data);
+ g_object_unref (thread_data->connection);
+ g_main_context_pop_thread_default (thread_context);
+ g_main_context_unref (thread_context);
+
+ g_mutex_lock (&thread_data->mutex);
+ g_assert_cmpint (thread_data->watch_id, ==, 0);
+ g_mutex_unlock (&thread_data->mutex);
+ return NULL;
+}
+
+static void
+watch_with_different_context (gboolean unwatch_early)
+{
+ OwnNameData own_data;
+ WatchNameThreadData thread_data;
+ GDBusConnection *connection;
+ GThread *watcher;
+ guint id;
+
+ session_bus_up ();
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert (connection != NULL);
+
+ g_mutex_init (&thread_data.mutex);
+ g_mutex_init (&thread_data.cond_mutex);
+ g_cond_init (&thread_data.cond);
+ thread_data.started = FALSE;
+ thread_data.name_acquired = FALSE;
+ thread_data.ended = FALSE;
+ thread_data.connection = g_object_ref (connection);
+ thread_data.unwatch_early = unwatch_early;
+
+ // Create a thread which will watch a name and wait for it to be ready
+ g_mutex_lock (&thread_data.cond_mutex);
+ watcher = g_thread_new ("watcher", watcher_thread, &thread_data);
+ while (!g_atomic_int_get (&thread_data.started))
+ g_cond_wait (&thread_data.cond, &thread_data.cond_mutex);
+ g_mutex_unlock (&thread_data.cond_mutex);
+
+ own_data.num_acquired = 0;
+ own_data.num_lost = 0;
+ own_data.num_free_func = 0;
+ own_data.expect_null_connection = FALSE;
+ // Own the name to avoid direct name vanished in watcher thread
+ id = g_bus_own_name_on_connection (connection,
+ "org.gtk.GDBus.Name1",
+ G_BUS_NAME_OWNER_FLAGS_REPLACE,
+ w_name_acquired_handler,
+ w_name_lost_handler,
+ &own_data,
+ (GDestroyNotify) own_name_data_free_func);
+ while (own_data.num_acquired == 0)
+ g_main_context_iteration (NULL, TRUE);
+ g_assert_cmpint (own_data.num_acquired, ==, 1);
+ g_assert_cmpint (own_data.num_lost, ==, 0);
+
+ // Wake the thread for it to begin watch
+ g_mutex_lock (&thread_data.cond_mutex);
+ g_atomic_int_set (&thread_data.name_acquired, TRUE);
+ g_cond_signal (&thread_data.cond);
+ g_mutex_unlock (&thread_data.cond_mutex);
+
+ // Iterate the loop until thread is waking us up
+ while (!thread_data.ended)
+ g_main_context_iteration (NULL, TRUE);
+
+ g_thread_join (watcher);
+
+ g_bus_unown_name (id);
+ while (own_data.num_free_func == 0)
+ g_main_context_iteration (NULL, TRUE);
+ g_assert_cmpint (own_data.num_free_func, ==, 1);
+
+ g_mutex_clear (&thread_data.mutex);
+ g_mutex_clear (&thread_data.cond_mutex);
+ g_cond_clear (&thread_data.cond);
+
+ session_bus_stop ();
+ g_assert_true (g_dbus_connection_is_closed (connection));
+ g_object_unref (connection);
session_bus_down ();
}
+static void
+test_bus_watch_different_context (void)
+{
+ watch_with_different_context (FALSE);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_bus_unwatch_early (void)
+{
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/604");
+ watch_with_different_context (TRUE);
+}
+
/* ---------------------------------------------------------------------------------------------------- */
static void
@@ -899,9 +1162,15 @@ test_validate_names (void)
g_assert (!g_dbus_is_unique_name (names[n].string));
if (names[n].interface)
- g_assert (g_dbus_is_interface_name (names[n].string));
+ {
+ g_assert (g_dbus_is_interface_name (names[n].string));
+ g_assert (g_dbus_is_error_name (names[n].string));
+ }
else
- g_assert (!g_dbus_is_interface_name (names[n].string));
+ {
+ g_assert (!g_dbus_is_interface_name (names[n].string));
+ g_assert (!g_dbus_is_error_name (names[n].string));
+ }
}
}
@@ -979,6 +1248,8 @@ main (int argc,
g_test_add_data_func ("/gdbus/bus-watch-name-closures-auto-start",
&watch_closures_flags_auto_start,
test_bus_watch_name);
+ g_test_add_func ("/gdbus/bus-watch-different-context", test_bus_watch_different_context);
+ g_test_add_func ("/gdbus/bus-unwatch-early", test_bus_unwatch_early);
g_test_add_func ("/gdbus/escape-object-path", test_escape_object_path);
ret = g_test_run();