Merge branch '1515-gdbus-threading' into 'master'

Resolve "gio/gdbus-threading test sometimes fails in CI"

Closes #1515

See merge request GNOME/glib!1375
This commit is contained in:
Sebastian Dröge 2020-02-24 10:00:57 +00:00
commit 73d557981d
10 changed files with 288 additions and 238 deletions

View File

@ -222,7 +222,6 @@ typedef struct
{
GDestroyNotify callback;
gpointer user_data;
GMainContext *context;
} CallDestroyNotifyData;
static gboolean
@ -236,8 +235,6 @@ call_destroy_notify_data_in_idle (gpointer user_data)
static void
call_destroy_notify_data_free (CallDestroyNotifyData *data)
{
if (data->context != NULL)
g_main_context_unref (data->context);
g_free (data);
}
@ -258,14 +255,11 @@ call_destroy_notify (GMainContext *context,
CallDestroyNotifyData *data;
if (callback == NULL)
goto out;
return;
data = g_new0 (CallDestroyNotifyData, 1);
data->callback = callback;
data->user_data = user_data;
data->context = context;
if (data->context != NULL)
g_main_context_ref (data->context);
idle_source = g_idle_source_new ();
g_source_set_priority (idle_source, G_PRIORITY_DEFAULT);
@ -274,11 +268,8 @@ call_destroy_notify (GMainContext *context,
data,
(GDestroyNotify) call_destroy_notify_data_free);
g_source_set_name (idle_source, "[gio] call_destroy_notify_data_in_idle");
g_source_attach (idle_source, data->context);
g_source_attach (idle_source, context);
g_source_unref (idle_source);
out:
;
}
/* ---------------------------------------------------------------------------------------------------- */
@ -3703,6 +3694,14 @@ unsubscribe_id_internal (GDBusConnection *connection,
*
* Unsubscribes from signals.
*
* Note that there may still be D-Bus traffic to process (relating to this
* signal subscription) in the current thread-default #GMainContext after this
* function has returned. You should continue to iterate the #GMainContext
* until the #GDestroyNotify function passed to
* g_dbus_connection_signal_subscribe() is called, in order to avoid memory
* leaks through callbacks queued on the #GMainContext after its stopped being
* iterated.
*
* Since: 2.26
*/
void

View File

@ -889,6 +889,13 @@ g_bus_own_name_on_connection_with_closures (GDBusConnection *connection,
*
* Stops owning a name.
*
* Note that there may still be D-Bus traffic to process (relating to owning
* and unowning the name) in the current thread-default #GMainContext after
* this function has returned. You should continue to iterate the #GMainContext
* until the #GDestroyNotify function passed to g_bus_own_name() is called, in
* order to avoid memory leaks through callbacks queued on the #GMainContext
* after its stopped being iterated.
*
* Since: 2.26
*/
void

View File

@ -858,6 +858,13 @@ guint g_bus_watch_name_on_connection_with_closures (
*
* Stops watching a name.
*
* Note that there may still be D-Bus traffic to process (relating to watching
* and unwatching the name) in the current thread-default #GMainContext after
* this function has returned. You should continue to iterate the #GMainContext
* until the #GDestroyNotify function passed to g_bus_watch_name() is called, in
* order to avoid memory leaks through callbacks queued on the #GMainContext
* after its stopped being iterated.
*
* Since: 2.26
*/
void

View File

@ -95,28 +95,19 @@ G_LOCK_DEFINE_STATIC (properties_lock);
/* ---------------------------------------------------------------------------------------------------- */
G_LOCK_DEFINE_STATIC (signal_subscription_lock);
typedef struct
static GWeakRef *
weak_ref_new (GObject *object)
{
volatile gint ref_count;
GDBusProxy *proxy;
} SignalSubscriptionData;
static SignalSubscriptionData *
signal_subscription_ref (SignalSubscriptionData *data)
{
g_atomic_int_inc (&data->ref_count);
return data;
GWeakRef *weak_ref = g_new0 (GWeakRef, 1);
g_weak_ref_init (weak_ref, object);
return g_steal_pointer (&weak_ref);
}
static void
signal_subscription_unref (SignalSubscriptionData *data)
weak_ref_free (GWeakRef *weak_ref)
{
if (g_atomic_int_dec_and_test (&data->ref_count))
{
g_slice_free (SignalSubscriptionData, data);
}
g_weak_ref_clear (weak_ref);
g_free (weak_ref);
}
/* ---------------------------------------------------------------------------------------------------- */
@ -152,8 +143,6 @@ struct _GDBusProxyPrivate
/* mutable, protected by properties_lock */
GDBusObject *object;
SignalSubscriptionData *signal_subscription_data;
};
enum
@ -189,22 +178,6 @@ G_DEFINE_TYPE_WITH_CODE (GDBusProxy, g_dbus_proxy, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init))
static void
g_dbus_proxy_dispose (GObject *object)
{
GDBusProxy *proxy = G_DBUS_PROXY (object);
G_LOCK (signal_subscription_lock);
if (proxy->priv->signal_subscription_data != NULL)
{
proxy->priv->signal_subscription_data->proxy = NULL;
signal_subscription_unref (proxy->priv->signal_subscription_data);
proxy->priv->signal_subscription_data = NULL;
}
G_UNLOCK (signal_subscription_lock);
G_OBJECT_CLASS (g_dbus_proxy_parent_class)->dispose (object);
}
static void
g_dbus_proxy_finalize (GObject *object)
{
@ -346,7 +319,6 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = g_dbus_proxy_dispose;
gobject_class->finalize = g_dbus_proxy_finalize;
gobject_class->set_property = g_dbus_proxy_set_property;
gobject_class->get_property = g_dbus_proxy_get_property;
@ -638,9 +610,6 @@ static void
g_dbus_proxy_init (GDBusProxy *proxy)
{
proxy->priv = g_dbus_proxy_get_instance_private (proxy);
proxy->priv->signal_subscription_data = g_slice_new0 (SignalSubscriptionData);
proxy->priv->signal_subscription_data->ref_count = 1;
proxy->priv->signal_subscription_data->proxy = proxy;
proxy->priv->properties = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
@ -868,21 +837,12 @@ on_signal_received (GDBusConnection *connection,
GVariant *parameters,
gpointer user_data)
{
SignalSubscriptionData *data = user_data;
GWeakRef *proxy_weak = user_data;
GDBusProxy *proxy;
G_LOCK (signal_subscription_lock);
proxy = data->proxy;
proxy = G_DBUS_PROXY (g_weak_ref_get (proxy_weak));
if (proxy == NULL)
{
G_UNLOCK (signal_subscription_lock);
return;
}
else
{
g_object_ref (proxy);
G_UNLOCK (signal_subscription_lock);
}
return;
if (!proxy->priv->initialized)
goto out;
@ -929,8 +889,7 @@ on_signal_received (GDBusConnection *connection,
parameters);
out:
if (proxy != NULL)
g_object_unref (proxy);
g_clear_object (&proxy);
}
/* ---------------------------------------------------------------------------------------------------- */
@ -1038,7 +997,7 @@ on_properties_changed (GDBusConnection *connection,
GVariant *parameters,
gpointer user_data)
{
SignalSubscriptionData *data = user_data;
GWeakRef *proxy_weak = user_data;
gboolean emit_g_signal = FALSE;
GDBusProxy *proxy;
const gchar *interface_name_for_signal;
@ -1052,18 +1011,9 @@ on_properties_changed (GDBusConnection *connection,
changed_properties = NULL;
invalidated_properties = NULL;
G_LOCK (signal_subscription_lock);
proxy = data->proxy;
proxy = G_DBUS_PROXY (g_weak_ref_get (proxy_weak));
if (proxy == NULL)
{
G_UNLOCK (signal_subscription_lock);
goto out;
}
else
{
g_object_ref (proxy);
G_UNLOCK (signal_subscription_lock);
}
return;
if (!proxy->priv->initialized)
goto out;
@ -1150,11 +1100,9 @@ on_properties_changed (GDBusConnection *connection,
}
out:
if (changed_properties != NULL)
g_variant_unref (changed_properties);
g_clear_pointer (&changed_properties, g_variant_unref);
g_free (invalidated_properties);
if (proxy != NULL)
g_object_unref (proxy);
g_clear_object (&proxy);
}
/* ---------------------------------------------------------------------------------------------------- */
@ -1258,8 +1206,7 @@ on_name_owner_changed_get_all_cb (GDBusConnection *connection,
{
G_LOCK (properties_lock);
g_free (data->proxy->priv->name_owner);
data->proxy->priv->name_owner = data->name_owner;
data->name_owner = NULL; /* to avoid an extra copy, we steal the string */
data->proxy->priv->name_owner = g_steal_pointer (&data->name_owner);
g_hash_table_remove_all (data->proxy->priv->properties);
G_UNLOCK (properties_lock);
if (result != NULL)
@ -1289,23 +1236,14 @@ on_name_owner_changed (GDBusConnection *connection,
GVariant *parameters,
gpointer user_data)
{
SignalSubscriptionData *data = user_data;
GWeakRef *proxy_weak = user_data;
GDBusProxy *proxy;
const gchar *old_owner;
const gchar *new_owner;
G_LOCK (signal_subscription_lock);
proxy = data->proxy;
proxy = G_DBUS_PROXY (g_weak_ref_get (proxy_weak));
if (proxy == NULL)
{
G_UNLOCK (signal_subscription_lock);
goto out;
}
else
{
g_object_ref (proxy);
G_UNLOCK (signal_subscription_lock);
}
return;
/* if we are already trying to load properties, cancel that */
if (proxy->priv->get_all_cancellable != NULL)
@ -1415,8 +1353,7 @@ on_name_owner_changed (GDBusConnection *connection,
}
out:
if (proxy != NULL)
g_object_unref (proxy);
g_clear_object (&proxy);
}
/* ---------------------------------------------------------------------------------------------------- */
@ -1762,8 +1699,8 @@ async_initable_init_first (GAsyncInitable *initable)
proxy->priv->interface_name,
G_DBUS_SIGNAL_FLAGS_NONE,
on_properties_changed,
signal_subscription_ref (proxy->priv->signal_subscription_data),
(GDestroyNotify) signal_subscription_unref);
weak_ref_new (G_OBJECT (proxy)),
(GDestroyNotify) weak_ref_free);
}
if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS))
@ -1778,8 +1715,8 @@ async_initable_init_first (GAsyncInitable *initable)
NULL, /* arg0 */
G_DBUS_SIGNAL_FLAGS_NONE,
on_signal_received,
signal_subscription_ref (proxy->priv->signal_subscription_data),
(GDestroyNotify) signal_subscription_unref);
weak_ref_new (G_OBJECT (proxy)),
(GDestroyNotify) weak_ref_free);
}
if (proxy->priv->name != NULL &&
@ -1794,8 +1731,8 @@ async_initable_init_first (GAsyncInitable *initable)
proxy->priv->name, /* arg0 */
G_DBUS_SIGNAL_FLAGS_NONE,
on_name_owner_changed,
signal_subscription_ref (proxy->priv->signal_subscription_data),
(GDestroyNotify) signal_subscription_unref);
weak_ref_new (G_OBJECT (proxy)),
(GDestroyNotify) weak_ref_free);
}
}

View File

@ -25,7 +25,7 @@
#include <gio/gio.h>
#include <gio/gunixmounts.h>
static void
static gboolean
run (GError **error,
const gchar *argv0,
...)
@ -34,6 +34,8 @@ run (GError **error,
const gchar *arg;
va_list ap;
GSubprocess *subprocess;
gchar *command_line = NULL;
gboolean success;
args = g_ptr_array_new ();
@ -44,14 +46,20 @@ run (GError **error,
g_ptr_array_add (args, NULL);
va_end (ap);
command_line = g_strjoinv (" ", (gchar **) args->pdata);
g_test_message ("Running command `%s`", command_line);
g_free (command_line);
subprocess = g_subprocess_newv ((const gchar * const *) args->pdata, G_SUBPROCESS_FLAGS_NONE, error);
g_ptr_array_free (args, TRUE);
if (subprocess == NULL)
return;
return FALSE;
g_subprocess_wait_check (subprocess, NULL, error);
success = g_subprocess_wait_check (subprocess, NULL, error);
g_object_unref (subprocess);
return success;
}
static void
@ -135,8 +143,14 @@ test_filesystem_readonly (gconstpointer with_mount_monitor)
/* Use bindfs, which does not need root privileges, to mount the contents of one dir
* into another dir (and do the mount as readonly as per passed '-o ro' option) */
run (&error, bindfs, "-n", "-o", "ro", dir_to_mount, dir_mountpoint, NULL);
g_assert_no_error (error);
if (!run (&error, bindfs, "-n", "-o", "ro", dir_to_mount, dir_mountpoint, NULL))
{
gchar *skip_message = g_strdup_printf ("Failed to run bindfs to set up test: %s", error->message);
g_test_skip (skip_message);
g_free (skip_message);
g_clear_error (&error);
return;
}
/* Let's check now, that the file is in indeed in a readonly filesystem */
file_in_mountpoint = g_strdup_printf ("%s/example.txt", dir_mountpoint);

View File

@ -124,14 +124,14 @@ main (int argc,
g_assert (g_spawn_command_line_async (path, NULL));
g_free (path);
ensure_gdbus_testserver_up ();
/* Create the connection in the main thread */
error = NULL;
c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
g_assert_no_error (error);
g_assert (c != NULL);
ensure_gdbus_testserver_up (c, NULL);
g_test_add_func ("/gdbus/connection-loss", test_connection_loss);
ret = g_test_run();

View File

@ -86,49 +86,72 @@ _give_up (gpointer data)
g_return_val_if_reached (TRUE);
}
void
ensure_gdbus_testserver_up (void)
typedef struct
{
guint id;
gchar *name_owner;
GDBusConnection *connection;
GDBusProxy *proxy;
GError *error = NULL;
GMainContext *context;
gboolean name_appeared;
gboolean unwatch_complete;
} WatchData;
connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
NULL,
&error);
static void
name_appeared_cb (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
WatchData *data = user_data;
g_assert_no_error (error);
error = NULL;
g_assert (name_owner != NULL);
data->name_appeared = TRUE;
g_main_context_wakeup (data->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);
static void
watch_free_cb (gpointer user_data)
{
WatchData *data = user_data;
id = g_timeout_add_seconds (60, _give_up,
"waited more than ~ 60s for gdbus-testserver to take its bus name");
data->unwatch_complete = TRUE;
g_main_context_wakeup (data->context);
}
while (TRUE)
{
name_owner = g_dbus_proxy_get_name_owner (proxy);
void
ensure_gdbus_testserver_up (GDBusConnection *connection,
GMainContext *context)
{
GSource *timeout_source = NULL;
guint watch_id;
WatchData data = { context, FALSE, FALSE };
if (name_owner != NULL)
break;
g_main_context_push_thread_default (context);
g_main_context_iteration (NULL, TRUE);
}
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);
g_source_remove (id);
g_free (name_owner);
g_object_unref (proxy);
g_object_unref (connection);
timeout_source = g_timeout_source_new_seconds (60);
g_source_set_callback (timeout_source, _give_up,
"waited more than ~ 60s for gdbus-testserver to take its bus name",
NULL);
g_source_attach (timeout_source, context);
while (!data.name_appeared)
g_main_context_iteration (context, TRUE);
g_bus_unwatch_name (watch_id);
watch_id = 0;
while (!data.unwatch_complete)
g_main_context_iteration (context, TRUE);
g_source_destroy (timeout_source);
g_source_unref (timeout_source);
g_main_context_pop_thread_default (context);
}
/* ---------------------------------------------------------------------------------------------------- */

View File

@ -114,7 +114,8 @@ GDBusConnection *_g_bus_get_priv (GBusType bus_type,
GCancellable *cancellable,
GError **error);
void ensure_gdbus_testserver_up (void);
void ensure_gdbus_testserver_up (GDBusConnection *connection,
GMainContext *context);
G_END_DECLS

View File

@ -27,59 +27,77 @@
/* all tests rely on a global connection */
static GDBusConnection *c = NULL;
typedef struct
{
GMainContext *context;
gboolean timed_out;
} TimeoutData;
static gboolean
timeout_cb (gpointer user_data)
{
TimeoutData *data = user_data;
data->timed_out = TRUE;
g_main_context_wakeup (data->context);
return G_SOURCE_REMOVE;
}
/* Check that the given @connection has only one ref, waiting to let any pending
* unrefs complete first. This is typically used on the shared connection, to
* ensure its in a correct state before beginning the next test. */
static void
assert_connection_has_one_ref (GDBusConnection *connection,
GMainContext *context)
{
GSource *timeout_source = NULL;
TimeoutData data = { context, FALSE };
if (g_atomic_int_get (&G_OBJECT (connection)->ref_count) == 1)
return;
timeout_source = g_timeout_source_new_seconds (1);
g_source_set_callback (timeout_source, timeout_cb, &data, NULL);
g_source_attach (timeout_source, context);
while (g_atomic_int_get (&G_OBJECT (connection)->ref_count) != 1 && !data.timed_out)
{
g_debug ("refcount of %p is not right, sleeping", connection);
g_main_context_iteration (NULL, TRUE);
}
g_source_destroy (timeout_source);
g_source_unref (timeout_source);
if (g_atomic_int_get (&G_OBJECT (connection)->ref_count) != 1)
g_error ("connection %p had too many refs", connection);
}
/* ---------------------------------------------------------------------------------------------------- */
/* Ensure that signal and method replies are delivered in the right thread */
/* ---------------------------------------------------------------------------------------------------- */
typedef struct {
GThread *thread;
GMainLoop *thread_loop;
GMainContext *context;
guint signal_count;
gboolean unsubscribe_complete;
GAsyncResult *async_result;
} DeliveryData;
static void
msg_cb_expect_success (GDBusConnection *connection,
GAsyncResult *res,
gpointer user_data)
async_result_cb (GDBusConnection *connection,
GAsyncResult *res,
gpointer user_data)
{
DeliveryData *data = user_data;
GError *error;
GVariant *result;
error = NULL;
result = g_dbus_connection_call_finish (connection,
res,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_variant_unref (result);
data->async_result = g_object_ref (res);
g_assert (g_thread_self () == data->thread);
g_assert_true (g_thread_self () == data->thread);
g_main_loop_quit (data->thread_loop);
}
static void
msg_cb_expect_error_cancelled (GDBusConnection *connection,
GAsyncResult *res,
gpointer user_data)
{
DeliveryData *data = user_data;
GError *error;
GVariant *result;
error = NULL;
result = g_dbus_connection_call_finish (connection,
res,
&error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
g_assert (!g_dbus_error_is_remote_error (error));
g_error_free (error);
g_assert (result == NULL);
g_assert (g_thread_self () == data->thread);
g_main_loop_quit (data->thread_loop);
g_main_context_wakeup (data->context);
}
static void
@ -93,33 +111,43 @@ signal_handler (GDBusConnection *connection,
{
DeliveryData *data = user_data;
g_assert (g_thread_self () == data->thread);
g_assert_true (g_thread_self () == data->thread);
data->signal_count++;
g_main_loop_quit (data->thread_loop);
g_main_context_wakeup (data->context);
}
static void
signal_data_free_cb (gpointer user_data)
{
DeliveryData *data = user_data;
g_assert_true (g_thread_self () == data->thread);
data->unsubscribe_complete = TRUE;
g_main_context_wakeup (data->context);
}
static gpointer
test_delivery_in_thread_func (gpointer _data)
{
GMainLoop *thread_loop;
GMainContext *thread_context;
DeliveryData data;
GCancellable *ca;
guint subscription_id;
GDBusConnection *priv_c;
GError *error;
error = NULL;
GError *error = NULL;
GVariant *result_variant = NULL;
thread_context = g_main_context_new ();
thread_loop = g_main_loop_new (thread_context, FALSE);
g_main_context_push_thread_default (thread_context);
data.thread = g_thread_self ();
data.thread_loop = thread_loop;
data.context = thread_context;
data.signal_count = 0;
data.unsubscribe_complete = FALSE;
data.async_result = NULL;
/* ---------------------------------------------------------------------------------------------------- */
@ -135,9 +163,16 @@ test_delivery_in_thread_func (gpointer _data)
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) msg_cb_expect_success,
(GAsyncReadyCallback) async_result_cb,
&data);
g_main_loop_run (thread_loop);
while (data.async_result == NULL)
g_main_context_iteration (thread_context, TRUE);
result_variant = g_dbus_connection_call_finish (c, data.async_result, &error);
g_assert_no_error (error);
g_assert_nonnull (result_variant);
g_clear_pointer (&result_variant, g_variant_unref);
g_clear_object (&data.async_result);
/*
* Check that we never actually send a message if the GCancellable
@ -155,9 +190,18 @@ test_delivery_in_thread_func (gpointer _data)
G_DBUS_CALL_FLAGS_NONE,
-1,
ca,
(GAsyncReadyCallback) msg_cb_expect_error_cancelled,
(GAsyncReadyCallback) async_result_cb,
&data);
g_main_loop_run (thread_loop);
while (data.async_result == NULL)
g_main_context_iteration (thread_context, TRUE);
result_variant = g_dbus_connection_call_finish (c, data.async_result, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
g_assert_false (g_dbus_error_is_remote_error (error));
g_clear_error (&error);
g_assert_null (result_variant);
g_clear_object (&data.async_result);
g_object_unref (ca);
/*
@ -173,47 +217,74 @@ test_delivery_in_thread_func (gpointer _data)
G_DBUS_CALL_FLAGS_NONE,
-1,
ca,
(GAsyncReadyCallback) msg_cb_expect_error_cancelled,
(GAsyncReadyCallback) async_result_cb,
&data);
g_cancellable_cancel (ca);
g_main_loop_run (thread_loop);
while (data.async_result == NULL)
g_main_context_iteration (thread_context, TRUE);
result_variant = g_dbus_connection_call_finish (c, data.async_result, &error);
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
g_assert_false (g_dbus_error_is_remote_error (error));
g_clear_error (&error);
g_assert_null (result_variant);
g_clear_object (&data.async_result);
g_object_unref (ca);
/*
* Check that signals are delivered to the correct thread.
*
* First we subscribe to the signal, then we create a a private
* connection. This should cause a NameOwnerChanged message from
* the message bus.
* First we subscribe to the signal, then we call EmitSignal(). This should
* cause a TestSignal emission from the testserver.
*/
subscription_id = g_dbus_connection_signal_subscribe (c,
"org.freedesktop.DBus", /* sender */
"org.freedesktop.DBus", /* interface */
"NameOwnerChanged", /* member */
"/org/freedesktop/DBus", /* path */
"com.example.TestService", /* sender */
"com.example.Frob", /* interface */
"TestSignal", /* member */
"/com/example/TestObject", /* path */
NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
signal_handler,
&data,
NULL);
g_assert (subscription_id != 0);
g_assert (data.signal_count == 0);
signal_data_free_cb);
g_assert_cmpuint (subscription_id, !=, 0);
g_assert_cmpuint (data.signal_count, ==, 0);
priv_c = _g_bus_get_priv (G_BUS_TYPE_SESSION, NULL, &error);
g_dbus_connection_call (c,
"com.example.TestService", /* bus_name */
"/com/example/TestObject", /* object path */
"com.example.Frob", /* interface name */
"EmitSignal", /* method name */
g_variant_new_parsed ("('hello', @o '/com/example/TestObject')"),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback) async_result_cb,
&data);
while (data.async_result == NULL || data.signal_count < 1)
g_main_context_iteration (thread_context, TRUE);
result_variant = g_dbus_connection_call_finish (c, data.async_result, &error);
g_assert_no_error (error);
g_assert (priv_c != NULL);
g_assert_nonnull (result_variant);
g_clear_pointer (&result_variant, g_variant_unref);
g_clear_object (&data.async_result);
g_main_loop_run (thread_loop);
g_assert (data.signal_count == 1);
g_object_unref (priv_c);
g_assert_cmpuint (data.signal_count, ==, 1);
g_dbus_connection_signal_unsubscribe (c, subscription_id);
subscription_id = 0;
while (!data.unsubscribe_complete)
g_main_context_iteration (thread_context, TRUE);
g_assert_true (data.unsubscribe_complete);
/* ---------------------------------------------------------------------------------------------------- */
g_main_context_pop_thread_default (thread_context);
g_main_loop_unref (thread_loop);
g_main_context_unref (thread_context);
return NULL;
@ -229,6 +300,8 @@ test_delivery_in_thread (void)
NULL);
g_thread_join (thread);
assert_connection_has_one_ref (c, NULL);
}
/* ---------------------------------------------------------------------------------------------------- */
@ -257,11 +330,11 @@ sleep_cb (GDBusProxy *proxy,
res,
&error);
g_assert_no_error (error);
g_assert (result != NULL);
g_assert_nonnull (result);
g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
g_variant_unref (result);
g_assert (data->thread == g_thread_self ());
g_assert_true (data->thread == g_thread_self ());
g_main_loop_quit (data->thread_loop);
@ -317,7 +390,7 @@ test_sleep_in_thread_func (gpointer _data)
g_printerr ("S");
//g_debug ("done invoking sync (%p)", g_thread_self ());
g_assert_no_error (error);
g_assert (result != NULL);
g_assert_nonnull (result);
g_assert_cmpstr (g_variant_get_type_string (result), ==, "()");
g_variant_unref (result);
}
@ -441,6 +514,8 @@ test_method_calls_in_thread (void)
if (g_test_verbose ())
g_printerr ("\n");
assert_connection_has_one_ref (c, NULL);
}
#define SLEEP_MIN_USEC 1
@ -457,8 +532,8 @@ ensure_connection_works (GDBusConnection *conn)
"/org/freedesktop/DBus", "org.freedesktop.DBus", "GetId", NULL, NULL, 0, -1,
NULL, &error);
g_assert_no_error (error);
g_assert (v != NULL);
g_assert (g_variant_is_of_type (v, G_VARIANT_TYPE ("(s)")));
g_assert_nonnull (v);
g_assert_true (g_variant_is_of_type (v, G_VARIANT_TYPE ("(s)")));
g_variant_unref (v);
}
@ -505,24 +580,11 @@ test_threaded_singleton (void)
for (i = 0; i < n; i++)
{
GThread *thread;
guint j;
guint unref_delay, get_delay;
GDBusConnection *new_conn;
/* We want to be the last ref, so let it finish setting up */
for (j = 0; j < 100; j++)
{
guint r = (guint) g_atomic_int_get (&G_OBJECT (c)->ref_count);
if (r == 1)
break;
g_debug ("run %u: refcount is %u, sleeping", i, r);
g_usleep (1000);
}
if (j == 100)
g_error ("connection had too many refs");
assert_connection_has_one_ref (c, NULL);
if (g_test_verbose () && (i % (n/50)) == 0)
g_printerr ("%u%%\n", ((i * 100) / n));
@ -586,16 +648,16 @@ main (int argc,
/* this is safe; testserver will exit once the bus goes away */
path = g_test_build_filename (G_TEST_BUILT, "gdbus-testserver", NULL);
g_assert (g_spawn_command_line_async (path, NULL));
g_assert_true (g_spawn_command_line_async (path, NULL));
g_free (path);
ensure_gdbus_testserver_up ();
/* Create the connection in the main thread */
error = NULL;
c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
g_assert_no_error (error);
g_assert (c != NULL);
g_assert_nonnull (c);
ensure_gdbus_testserver_up (c, NULL);
g_test_add_func ("/gdbus/delivery-in-thread", test_delivery_in_thread);
g_test_add_func ("/gdbus/method-calls-in-thread", test_method_calls_in_thread);

View File

@ -302,7 +302,7 @@ if host_machine.system() != 'windows'
},
'gdbus-threading' : {
'extra_sources' : extra_sources,
'suite' : ['slow', 'flaky'],
'suite' : ['slow'],
},
'gmenumodel' : {
'extra_sources' : extra_sources,