X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgdbusconnection.c;h=7a4a08ae516512a65bfd0882ed170286d3f41136;hb=e3c7fdd7a47264c584f3f16e37d903a7d1f36f76;hp=62b92614f4495c408bbcbab7e63d2ff234cbcd01;hpb=7190af4394541c1adbe87769f7bd6db030e33605;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 62b9261..7a4a08a 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -23,11 +23,6 @@ /* * TODO for GDBus: * - * - Validate all data (e.g. UTF-8) and check all the required D-Bus headers - * are present and forbidden ones aren't - * - When writing: g_dbus_message_to_blob() - * - When reading: g_dbus_message_new_from_blob() - * * - would be nice to expose GDBusAuthMechanism and an extension point * * - Need to rewrite GDBusAuth and rework GDBusAuthMechanism. In particular @@ -52,7 +47,7 @@ * distribution) * * - (ideally) this requires D-Bus spec work because none of - * this has never really been specced out properly (excect + * this has never really been specced out properly (except * the X11 bits) * * - Related to the above, we also need to be able to launch a message bus @@ -62,7 +57,7 @@ * - probably want a G_DBUS_NONCE_TCP_TMPDIR environment variable * to specify where the nonce is stored. This will allow people to use * G_DBUS_NONCE_TCP_TMPDIR=/mnt/secure.company.server/dbus-nonce-dir - * to easily acheive secure RPC via nonce-tcp. + * to easily achieve secure RPC via nonce-tcp. * * - need to expose an extension point for resolving D-Bus address and * turning them into GIOStream objects. This will allow us to implement @@ -95,6 +90,15 @@ * kind of pitfalls it avoids * - Export objects before claiming names * - Talk about auto-starting services (cf. GBusNameWatcherFlags) + * + * - use abstract sockets in test code + * - right now it doesn't work, dbus-daemon(1) fails with + * + * /gdbus/connection/filter: Failed to start message bus: Failed to bind + * socket "/tmp/g-dbus-tests-pid-28531": Address already in use + * ** WARNING **: Error reading address from dbus daemon, 0 bytes read + * + * or similar. */ #include "config.h" @@ -117,7 +121,6 @@ #include "gdbusmethodinvocation.h" #include "gdbusprivate.h" #include "gdbusauthobserver.h" -#include "gio-marshal.h" #include "ginitable.h" #include "gasyncinitable.h" #include "giostream.h" @@ -145,6 +148,31 @@ * an D-Bus client, it is often easier to use the g_bus_own_name(), * g_bus_watch_name() or g_dbus_proxy_new_for_bus() APIs. * + * As an exception to the usual GLib rule that a particular object must not be + * used by two threads at the same time, #GDBusConnection's methods may be + * called from any thread + * + * This is so that g_bus_get() and g_bus_get_sync() can safely return the + * same #GDBusConnection when called from any thread. + * + * . + * + * Most of the ways to obtain a #GDBusConnection automatically initialize it + * (i.e. connect to D-Bus): for instance, g_dbus_connection_new() and + * g_bus_get(), and the synchronous versions of those methods, give you an + * initialized connection. Language bindings for GIO should use + * g_initable_new() or g_async_initable_new_async(), which also initialize the + * connection. + * + * If you construct an uninitialized #GDBusConnection, such as via + * g_object_new(), you must initialize it via g_initable_init() or + * g_async_initable_init_async() before using its methods or properties. + * Calling methods or accessing properties on a #GDBusConnection that has not + * completed initialization successfully is considered to be invalid, and leads + * to undefined behaviour. In particular, if initialization fails with a + * #GError, the only valid thing you can do with that #GDBusConnection is to + * free it with g_object_unref(). + * * D-Bus server exampleFIXME: MISSING XINCLUDE CONTENT * * D-Bus subtree exampleFIXME: MISSING XINCLUDE CONTENT @@ -180,8 +208,83 @@ struct _GDBusConnectionClass G_LOCK_DEFINE_STATIC (message_bus_lock); -static GDBusConnection *the_session_bus = NULL; -static GDBusConnection *the_system_bus = NULL; +static GWeakRef the_session_bus; +static GWeakRef the_system_bus; + +/* Extra pseudo-member of GDBusSendMessageFlags. + * Set by initable_init() to indicate that despite not being initialized yet, + * enough of the only-valid-after-init members are set that we can send a + * message, and we're being called from its thread, so no memory barrier is + * required before accessing them. + */ +#define SEND_MESSAGE_FLAGS_INITIALIZING (1<<31) + +/* Same as SEND_MESSAGE_FLAGS_INITIALIZING, but in GDBusCallFlags */ +#define CALL_FLAGS_INITIALIZING (1<<31) + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GDestroyNotify callback; + gpointer user_data; + GMainContext *context; +} CallDestroyNotifyData; + +static gboolean +call_destroy_notify_data_in_idle (gpointer user_data) +{ + CallDestroyNotifyData *data = user_data; + data->callback (data->user_data); + return FALSE; +} + +static void +call_destroy_notify_data_free (CallDestroyNotifyData *data) +{ + if (data->context != NULL) + g_main_context_unref (data->context); + g_free (data); +} + +/* + * call_destroy_notify: + * @context: A #GMainContext or %NULL. + * @callback: A #GDestroyNotify or %NULL. + * @user_data: Data to pass to @callback. + * + * Schedules @callback to run in @context. + */ +static void +call_destroy_notify (GMainContext *context, + GDestroyNotify callback, + gpointer user_data) +{ + GSource *idle_source; + CallDestroyNotifyData *data; + + if (callback == NULL) + goto out; + + 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); + g_source_set_callback (idle_source, + call_destroy_notify_data_in_idle, + data, + (GDestroyNotify) call_destroy_notify_data_free); + g_source_attach (idle_source, data->context); + g_source_unref (idle_source); + + out: + ; +} /* ---------------------------------------------------------------------------------------------------- */ @@ -206,7 +309,7 @@ _g_strv_has_string (const gchar* const *haystack, #else // TODO: for some reason this doesn't work on Windows #define CONNECTION_ENSURE_LOCK(obj) do { \ - if (G_UNLIKELY (g_mutex_trylock((obj)->lock))) \ + if (G_UNLIKELY (g_mutex_trylock(&(obj)->lock))) \ { \ g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ "CONNECTION_ENSURE_LOCK: GDBusConnection object lock is not locked"); \ @@ -215,13 +318,20 @@ _g_strv_has_string (const gchar* const *haystack, #endif #define CONNECTION_LOCK(obj) do { \ - g_mutex_lock ((obj)->lock); \ + g_mutex_lock (&(obj)->lock); \ } while (FALSE) #define CONNECTION_UNLOCK(obj) do { \ - g_mutex_unlock ((obj)->lock); \ + g_mutex_unlock (&(obj)->lock); \ } while (FALSE) +/* Flags in connection->atomic_flags */ +enum { + FLAG_INITIALIZED = 1 << 0, + FLAG_EXIT_ON_CLOSE = 1 << 1, + FLAG_CLOSED = 1 << 2 +}; + /** * GDBusConnection: * @@ -239,85 +349,120 @@ struct _GDBusConnection /* -- General object state ------------------------------------------------ */ /* ------------------------------------------------------------------------ */ - /* object-wide lock */ - GMutex *lock; + /* General-purpose lock for most fields */ + GMutex lock; /* A lock used in the init() method of the GInitable interface - see comments - * in initable_init() for why a separate lock is needed + * in initable_init() for why a separate lock is needed. + * + * If you need both @lock and @init_lock, you must take @init_lock first. */ - GMutex *init_lock; + GMutex init_lock; /* Set (by loading the contents of /var/lib/dbus/machine-id) the first time - * someone calls org.freedesktop.DBus.GetMachineId() + * someone calls org.freedesktop.DBus.GetMachineId(). Protected by @lock. */ gchar *machine_id; - /* The underlying stream used for communication */ + /* The underlying stream used for communication + * Read-only after initable_init(), so it may be read if you either + * hold @init_lock or check for initialization first. + */ GIOStream *stream; - /* The object used for authentication (if any) */ + /* The object used for authentication (if any). + * Read-only after initable_init(), so it may be read if you either + * hold @init_lock or check for initialization first. + */ GDBusAuth *auth; - /* Set to TRUE if the connection has been closed */ - gboolean closed; - - /* Last serial used */ + /* Last serial used. Protected by @lock. */ guint32 last_serial; - /* The object used to send/receive message */ + /* The object used to send/receive messages. + * Read-only after initable_init(), so it may be read if you either + * hold @init_lock or check for initialization first. + */ GDBusWorker *worker; /* If connected to a message bus, this contains the unique name assigned to - * us by the bus (e.g. ":1.42") + * us by the bus (e.g. ":1.42"). + * Read-only after initable_init(), so it may be read if you either + * hold @init_lock or check for initialization first. */ gchar *bus_unique_name; /* The GUID returned by the other side if we authenticed as a client or - * the GUID to use if authenticating as a server + * the GUID to use if authenticating as a server. + * Read-only after initable_init(), so it may be read if you either + * hold @init_lock or check for initialization first. */ gchar *guid; - /* set to TRUE exactly when initable_init() has finished running */ - gboolean is_initialized; + /* FLAG_INITIALIZED is set exactly when initable_init() has finished running. + * Inspect @initialization_error to see whether it succeeded or failed. + * + * FLAG_EXIT_ON_CLOSE is the exit-on-close property. + * + * FLAG_CLOSED is the closed property. It may be read at any time, but + * may only be written while holding @lock. + */ + volatile gint atomic_flags; - /* If the connection could not be established during initable_init(), this GError will set */ + /* If the connection could not be established during initable_init(), + * this GError will be set. + * Read-only after initable_init(), so it may be read if you either + * hold @init_lock or check for initialization first. + */ GError *initialization_error; - /* The result of g_main_context_get_thread_default() when the object + /* The result of g_main_context_ref_thread_default() when the object * was created (the GObject _init() function) - this is used for delivery * of the :closed GObject signal. + * + * Only set in the GObject init function, so no locks are needed. */ GMainContext *main_context_at_construction; - /* construct properties */ + /* Read-only construct properties, no locks needed */ gchar *address; GDBusConnectionFlags flags; - /* Map used for managing method replies */ + /* Map used for managing method replies, protected by @lock */ GHashTable *map_method_serial_to_send_message_data; /* guint32 -> SendMessageData* */ - /* Maps used for managing signal subscription */ + /* Maps used for managing signal subscription, protected by @lock */ GHashTable *map_rule_to_signal_data; /* match rule (gchar*) -> SignalData */ GHashTable *map_id_to_signal_data; /* id (guint) -> SignalData */ GHashTable *map_sender_unique_name_to_signal_data_array; /* unique sender (gchar*) -> GPtrArray* of SignalData */ - /* Maps used for managing exported objects and subtrees */ + /* Maps used for managing exported objects and subtrees, + * protected by @lock + */ GHashTable *map_object_path_to_eo; /* gchar* -> ExportedObject* */ GHashTable *map_id_to_ei; /* guint -> ExportedInterface* */ GHashTable *map_object_path_to_es; /* gchar* -> ExportedSubtree* */ GHashTable *map_id_to_es; /* guint -> ExportedSubtree* */ - /* Structure used for message filters */ + /* Structure used for message filters, protected by @lock */ GPtrArray *filters; - /* Whether to exit on close */ - gboolean exit_on_close; - - /* Capabilities negotiated during authentication */ + /* Capabilities negotiated during authentication + * Read-only after initable_init(), so it may be read without holding a + * lock, if you check for initialization first. + */ GDBusCapabilityFlags capabilities; + /* Protected by @init_lock */ GDBusAuthObserver *authentication_observer; - GCredentials *crendentials; + + /* Read-only after initable_init(), so it may be read if you either + * hold @init_lock or check for initialization first. + */ + GCredentials *credentials; + + /* set to TRUE when finalizing */ + gboolean finalizing; }; typedef struct ExportedObject ExportedObject; @@ -377,26 +522,104 @@ G_DEFINE_TYPE_WITH_CODE (GDBusConnection, g_dbus_connection, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) ); -static void -g_dbus_connection_dispose (GObject *object) +/* + * Check that all members of @connection that can only be accessed after + * the connection is initialized can safely be accessed. If not, + * log a critical warning. This function is a memory barrier. + * + * Returns: %TRUE if initialized + */ +static gboolean +check_initialized (GDBusConnection *connection) { - GDBusConnection *connection = G_DBUS_CONNECTION (object); + /* The access to @atomic_flags isn't conditional, so that this function + * provides a memory barrier for thread-safety even if checks are disabled. + * (If you don't want this stricter guarantee, you can call + * g_return_if_fail (check_initialized (c)).) + * + * This isn't strictly necessary now that we've decided use of an + * uninitialized GDBusConnection is undefined behaviour, but it seems + * better to be as deterministic as is feasible. + * + * (Anything that could suffer a crash from seeing undefined values + * must have a race condition - thread A initializes the connection while + * thread B calls a method without initialization, hoping that thread A will + * win the race - so its behaviour is undefined anyway.) + */ + gint flags = g_atomic_int_get (&connection->atomic_flags); - G_LOCK (message_bus_lock); - //g_debug ("disposing %p", connection); - if (connection == the_session_bus) + g_return_val_if_fail (flags & FLAG_INITIALIZED, FALSE); + + /* We can safely access this, due to the memory barrier above */ + g_return_val_if_fail (connection->initialization_error == NULL, FALSE); + + return TRUE; +} + +typedef enum { + MAY_BE_UNINITIALIZED = (1<<1) +} CheckUnclosedFlags; + +/* + * Check the same thing as check_initialized(), and also that the + * connection is not closed. If the connection is uninitialized, + * raise a critical warning (it's programmer error); if it's closed, + * raise a recoverable GError (it's a runtime error). + * + * This function is a memory barrier. + * + * Returns: %TRUE if initialized and not closed + */ +static gboolean +check_unclosed (GDBusConnection *connection, + CheckUnclosedFlags check, + GError **error) +{ + /* check_initialized() is effectively inlined, so we don't waste time + * doing two memory barriers + */ + gint flags = g_atomic_int_get (&connection->atomic_flags); + + if (!(check & MAY_BE_UNINITIALIZED)) { - the_session_bus = NULL; + g_return_val_if_fail (flags & FLAG_INITIALIZED, FALSE); + g_return_val_if_fail (connection->initialization_error == NULL, FALSE); } - else if (connection == the_system_bus) + + if (flags & FLAG_CLOSED) { - the_system_bus = NULL; + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_CLOSED, + _("The connection is closed")); + return FALSE; } + + return TRUE; +} + +static GHashTable *alive_connections = NULL; + +static void +g_dbus_connection_dispose (GObject *object) +{ + GDBusConnection *connection = G_DBUS_CONNECTION (object); + + G_LOCK (message_bus_lock); + CONNECTION_LOCK (connection); if (connection->worker != NULL) { _g_dbus_worker_stop (connection->worker); connection->worker = NULL; + if (alive_connections != NULL) + g_warn_if_fail (g_hash_table_remove (alive_connections, connection)); + } + else + { + if (alive_connections != NULL) + g_warn_if_fail (g_hash_table_lookup (alive_connections, connection) == NULL); } + CONNECTION_UNLOCK (connection); G_UNLOCK (message_bus_lock); if (G_OBJECT_CLASS (g_dbus_connection_parent_class)->dispose != NULL) @@ -408,21 +631,24 @@ g_dbus_connection_finalize (GObject *object) { GDBusConnection *connection = G_DBUS_CONNECTION (object); + connection->finalizing = TRUE; + + purge_all_signal_subscriptions (connection); + + purge_all_filters (connection); + g_ptr_array_unref (connection->filters); + if (connection->authentication_observer != NULL) g_object_unref (connection->authentication_observer); if (connection->auth != NULL) g_object_unref (connection->auth); - //g_debug ("finalizing %p", connection); + if (connection->credentials) + g_object_unref (connection->credentials); + if (connection->stream != NULL) { - /* We don't really care if closing the stream succeeds or not */ - g_io_stream_close_async (connection->stream, - G_PRIORITY_DEFAULT, - NULL, /* GCancellable */ - NULL, /* GAsyncReadyCallback */ - NULL); /* userdata */ g_object_unref (connection->stream); connection->stream = NULL; } @@ -437,7 +663,6 @@ g_dbus_connection_finalize (GObject *object) g_hash_table_unref (connection->map_method_serial_to_send_message_data); - purge_all_signal_subscriptions (connection); g_hash_table_unref (connection->map_rule_to_signal_data); g_hash_table_unref (connection->map_id_to_signal_data); g_hash_table_unref (connection->map_sender_unique_name_to_signal_data_array); @@ -447,20 +672,17 @@ g_dbus_connection_finalize (GObject *object) g_hash_table_unref (connection->map_id_to_es); g_hash_table_unref (connection->map_object_path_to_es); - purge_all_filters (connection); - g_ptr_array_unref (connection->filters); - - if (connection->main_context_at_construction != NULL) - g_main_context_unref (connection->main_context_at_construction); + g_main_context_unref (connection->main_context_at_construction); g_free (connection->machine_id); - g_mutex_free (connection->init_lock); - g_mutex_free (connection->lock); + g_mutex_clear (&connection->init_lock); + g_mutex_clear (&connection->lock); G_OBJECT_CLASS (g_dbus_connection_parent_class)->finalize (object); } +/* called in any user thread, with the connection's lock not held */ static void g_dbus_connection_get_property (GObject *object, guint prop_id, @@ -501,6 +723,7 @@ g_dbus_connection_get_property (GObject *object, } } +/* called in any user thread, with the connection's lock not held */ static void g_dbus_connection_set_property (GObject *object, guint prop_id, @@ -541,14 +764,37 @@ g_dbus_connection_set_property (GObject *object, } } +/* Base-class implementation of GDBusConnection::closed. + * + * Called in a user thread, by the main context that was thread-default when + * the object was constructed. + */ static void g_dbus_connection_real_closed (GDBusConnection *connection, gboolean remote_peer_vanished, GError *error) { - if (remote_peer_vanished && connection->exit_on_close) + gint flags = g_atomic_int_get (&connection->atomic_flags); + + /* Because atomic int access is a memory barrier, we can safely read + * initialization_error without a lock, as long as we do it afterwards. + */ + if (remote_peer_vanished && + (flags & FLAG_EXIT_ON_CLOSE) != 0 && + (flags & FLAG_INITIALIZED) != 0 && + connection->initialization_error == NULL) { - g_print ("%s: Remote peer vanished. Exiting.\n", G_STRFUNC); + if (error != NULL) + { + g_print ("%s: Remote peer vanished with error: %s (%s, %d). Exiting.\n", + G_STRFUNC, + error->message, + g_quark_to_string (error->domain), error->code); + } + else + { + g_print ("%s: Remote peer vanished. Exiting.\n", G_STRFUNC); + } raise (SIGTERM); } } @@ -572,6 +818,13 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * * The underlying #GIOStream used for I/O. * + * If this is passed on construction and is a #GSocketConnection, + * then the corresponding #GSocket will be put into non-blocking mode. + * + * While the #GDBusConnection is active, it will interact with this + * stream from a worker thread, so it is not safe to interact with + * the stream directly. + * * Since: 2.26 */ g_object_class_install_property (gobject_class, @@ -703,6 +956,9 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * calling raise(SIGTERM)) if the connection * is closed by the remote peer. * + * Note that #GDBusConnection objects returned by g_bus_get_finish() and + * g_bus_get_sync() will (usually) have this property set to %TRUE. + * * Since: 2.26 */ g_object_class_install_property (gobject_class, @@ -794,7 +1050,7 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) G_STRUCT_OFFSET (GDBusConnectionClass, closed), NULL, NULL, - _gio_marshal_VOID__BOOLEAN_BOXED, + NULL, G_TYPE_NONE, 2, G_TYPE_BOOLEAN, @@ -804,8 +1060,8 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) static void g_dbus_connection_init (GDBusConnection *connection) { - connection->lock = g_mutex_new (); - connection->init_lock = g_mutex_new (); + g_mutex_init (&connection->lock); + g_mutex_init (&connection->init_lock); connection->map_method_serial_to_send_message_data = g_hash_table_new (g_direct_hash, g_direct_equal); @@ -816,7 +1072,7 @@ g_dbus_connection_init (GDBusConnection *connection) connection->map_sender_unique_name_to_signal_data_array = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - NULL); + (GDestroyNotify) g_ptr_array_unref); connection->map_object_path_to_eo = g_hash_table_new_full (g_str_hash, g_str_equal, @@ -834,9 +1090,7 @@ g_dbus_connection_init (GDBusConnection *connection) connection->map_id_to_es = g_hash_table_new (g_direct_hash, g_direct_equal); - connection->main_context_at_construction = g_main_context_get_thread_default (); - if (connection->main_context_at_construction != NULL) - g_main_context_ref (connection->main_context_at_construction); + connection->main_context_at_construction = g_main_context_ref_thread_default (); connection->filters = g_ptr_array_new (); } @@ -847,7 +1101,11 @@ g_dbus_connection_init (GDBusConnection *connection) * * Gets the underlying stream used for IO. * - * Returns: the stream used for IO + * While the #GDBusConnection is active, it will interact with this + * stream from a worker thread, so it is not safe to interact with + * the stream directly. + * + * Returns: (transfer none): the stream used for IO * * Since: 2.26 */ @@ -855,6 +1113,11 @@ GIOStream * g_dbus_connection_get_stream (GDBusConnection *connection) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + + /* do not use g_return_val_if_fail(), we want the memory barrier */ + if (!check_initialized (connection)) + return NULL; + return connection->stream; } @@ -873,6 +1136,12 @@ void g_dbus_connection_start_message_processing (GDBusConnection *connection) { g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + + /* do not use g_return_val_if_fail(), we want the memory barrier */ + if (!check_initialized (connection)) + return; + + g_assert (connection->worker != NULL); _g_dbus_worker_unfreeze (connection->worker); } @@ -889,8 +1158,13 @@ g_dbus_connection_start_message_processing (GDBusConnection *connection) gboolean g_dbus_connection_is_closed (GDBusConnection *connection) { + gint flags; + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); - return connection->closed; + + flags = g_atomic_int_get (&connection->atomic_flags); + + return (flags & FLAG_CLOSED) ? TRUE : FALSE; } /** @@ -907,11 +1181,17 @@ GDBusCapabilityFlags g_dbus_connection_get_capabilities (GDBusConnection *connection) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), G_DBUS_CAPABILITY_FLAGS_NONE); + + /* do not use g_return_val_if_fail(), we want the memory barrier */ + if (!check_initialized (connection)) + return G_DBUS_CAPABILITY_FLAGS_NONE; + return connection->capabilities; } /* ---------------------------------------------------------------------------------------------------- */ +/* Called in a temporary thread without holding locks. */ static void flush_in_thread_func (GSimpleAsyncResult *res, GObject *object, @@ -923,18 +1203,15 @@ flush_in_thread_func (GSimpleAsyncResult *res, if (!g_dbus_connection_flush_sync (G_DBUS_CONNECTION (object), cancellable, &error)) - { - g_simple_async_result_set_from_error (res, error); - g_error_free (error); - } + g_simple_async_result_take_error (res, error); } /** * g_dbus_connection_flush: * @connection: A #GDBusConnection. * @cancellable: A #GCancellable or %NULL. - * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't - * care about the result. + * @callback: (allow-none): A #GAsyncReadyCallback to call when the request is + * satisfied or %NULL if you don't care about the result. * @user_data: The data to pass to @callback. * * Asynchronously flushes @connection, that is, writes all queued @@ -965,10 +1242,11 @@ g_dbus_connection_flush (GDBusConnection *connection, g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); - simple = g_simple_async_result_new (NULL, + simple = g_simple_async_result_new (G_OBJECT (connection), callback, user_data, g_dbus_connection_flush); + g_simple_async_result_set_check_cancellable (simple, cancellable); g_simple_async_result_run_in_thread (simple, flush_in_thread_func, G_PRIORITY_DEFAULT, @@ -1040,14 +1318,17 @@ g_dbus_connection_flush_sync (GDBusConnection *connection, ret = FALSE; - if (connection->closed) - { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_CLOSED, - _("The connection is closed")); - goto out; - } + /* This is only a best-effort attempt to see whether the connection is + * closed, so it doesn't need the lock. If the connection closes just + * after this check, but before scheduling the flush operation, the + * result will be more or less the same as if the connection closed while + * the flush operation was pending - it'll fail with either CLOSED or + * CANCELLED. + */ + if (!check_unclosed (connection, 0, error)) + goto out; + + g_assert (connection->worker != NULL); ret = _g_dbus_worker_flush_sync (connection->worker, cancellable, @@ -1075,6 +1356,9 @@ emit_closed_data_free (EmitClosedData *data) g_free (data); } +/* Called in a user thread that has acquired the main context that was + * thread-default when the object was constructed + */ static gboolean emit_closed_in_idle (gpointer user_data) { @@ -1091,21 +1375,19 @@ emit_closed_in_idle (gpointer user_data) return FALSE; } -/* Can be called from any thread, must hold lock */ +/* Can be called from any thread, must hold lock. + * FLAG_CLOSED must already have been set. + */ static void -set_closed_unlocked (GDBusConnection *connection, - gboolean remote_peer_vanished, - GError *error) +schedule_closed_unlocked (GDBusConnection *connection, + gboolean remote_peer_vanished, + GError *error) { GSource *idle_source; EmitClosedData *data; CONNECTION_ENSURE_LOCK (connection); - g_assert (!connection->closed); - - connection->closed = TRUE; - data = g_new0 (EmitClosedData, 1); data->connection = g_object_ref (connection); data->remote_peer_vanished = remote_peer_vanished; @@ -1123,29 +1405,12 @@ set_closed_unlocked (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ -static void -close_in_thread_func (GSimpleAsyncResult *res, - GObject *object, - GCancellable *cancellable) -{ - GError *error; - - error = NULL; - if (!g_dbus_connection_close_sync (G_DBUS_CONNECTION (object), - cancellable, - &error)) - { - g_simple_async_result_set_from_error (res, error); - g_error_free (error); - } -} - /** * g_dbus_connection_close: * @connection: A #GDBusConnection. * @cancellable: A #GCancellable or %NULL. - * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't - * care about the result. + * @callback: (allow-none): A #GAsyncReadyCallback to call when the request is + * satisfied or %NULL if you don't care about the result. * @user_data: The data to pass to @callback. * * Closes @connection. Note that this never causes the process to @@ -1185,14 +1450,18 @@ g_dbus_connection_close (GDBusConnection *connection, g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); - simple = g_simple_async_result_new (NULL, + /* do not use g_return_val_if_fail(), we want the memory barrier */ + if (!check_initialized (connection)) + return; + + g_assert (connection->worker != NULL); + + simple = g_simple_async_result_new (G_OBJECT (connection), callback, user_data, g_dbus_connection_close); - g_simple_async_result_run_in_thread (simple, - close_in_thread_func, - G_PRIORITY_DEFAULT, - cancellable); + g_simple_async_result_set_check_cancellable (simple, cancellable); + _g_dbus_worker_close (connection->worker, cancellable, simple); g_object_unref (simple); } @@ -1233,6 +1502,23 @@ g_dbus_connection_close_finish (GDBusConnection *connection, return ret; } +typedef struct { + GMainLoop *loop; + GAsyncResult *result; +} SyncCloseData; + +/* Can be called by any thread, without the connection lock */ +static void +sync_close_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + SyncCloseData *data = user_data; + + data->result = g_object_ref (res); + g_main_loop_quit (data->loop); +} + /** * g_dbus_connection_close_sync: * @connection: A #GDBusConnection. @@ -1260,32 +1546,36 @@ g_dbus_connection_close_sync (GDBusConnection *connection, ret = FALSE; - CONNECTION_LOCK (connection); - if (!connection->closed) - { - ret = g_io_stream_close (connection->stream, - cancellable, - error); - if (ret) - set_closed_unlocked (connection, FALSE, NULL); - } - else + if (check_unclosed (connection, 0, error)) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_CLOSED, - _("The connection is closed")); + GMainContext *context; + SyncCloseData data; + + context = g_main_context_new (); + g_main_context_push_thread_default (context); + data.loop = g_main_loop_new (context, TRUE); + data.result = NULL; + + g_dbus_connection_close (connection, cancellable, sync_close_cb, &data); + g_main_loop_run (data.loop); + ret = g_dbus_connection_close_finish (connection, data.result, error); + + g_object_unref (data.result); + g_main_loop_unref (data.loop); + g_main_context_pop_thread_default (context); + g_main_context_unref (context); } - CONNECTION_UNLOCK (connection); return ret; } /* ---------------------------------------------------------------------------------------------------- */ +/* Can be called by any thread, with the connection lock held */ static gboolean g_dbus_connection_send_message_unlocked (GDBusConnection *connection, GDBusMessage *message, + GDBusSendMessageFlags flags, volatile guint32 *out_serial, GError **error) { @@ -1307,14 +1597,15 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, if (out_serial != NULL) *out_serial = 0; - if (connection->closed) - { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_CLOSED, - _("The connection is closed")); - goto out; - } + /* If we're in initable_init(), don't check for being initialized, to avoid + * chicken-and-egg problems. initable_init() is responsible for setting up + * our prerequisites (mainly connection->worker), and only calling us + * from its own thread (so no memory barrier is needed). + */ + if (!check_unclosed (connection, + (flags & SEND_MESSAGE_FLAGS_INITIALIZING) ? MAY_BE_UNINITIALIZED : 0, + error)) + goto out; blob = g_dbus_message_to_blob (message, &blob_size, @@ -1323,11 +1614,10 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, if (blob == NULL) goto out; - serial_to_use = g_dbus_message_get_serial (message); - if (serial_to_use == 0) - { - serial_to_use = ++connection->last_serial; /* TODO: handle overflow */ - } + if (flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) + serial_to_use = g_dbus_message_get_serial (message); + else + serial_to_use = ++connection->last_serial; /* TODO: handle overflow */ switch (blob[0]) { @@ -1355,8 +1645,10 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, if (out_serial != NULL) *out_serial = serial_to_use; - g_dbus_message_set_serial (message, serial_to_use); + if (!(flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL)) + g_dbus_message_set_serial (message, serial_to_use); + g_dbus_message_lock (message); _g_dbus_worker_send_message (connection->worker, message, (gchar*) blob, @@ -1375,16 +1667,18 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, * g_dbus_connection_send_message: * @connection: A #GDBusConnection. * @message: A #GDBusMessage - * @out_serial: Return location for serial number assigned to @message when sending it or %NULL. + * @flags: Flags affecting how the message is sent. + * @out_serial: (out) (allow-none): Return location for serial number assigned + * to @message when sending it or %NULL. * @error: Return location for error or %NULL. * * Asynchronously sends @message to the peer represented by @connection. * - * If g_dbus_message_get_serial() returns non-zero for @message, then - * that value is used for the message serial number. Otherwise a - * serial number will be assigned by @connection and set on @message - * via g_dbus_message_set_serial(). If @out_serial is not %NULL, then - * the serial number used will be written to this location prior to + * Unless @flags contain the + * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag, the serial number + * will be assigned by @connection and set on @message via + * g_dbus_message_set_serial(). If @out_serial is not %NULL, then the + * serial number used will be written to this location prior to * submitting the message to the underlying transport. * * If @connection is closed then the operation will fail with @@ -1395,6 +1689,9 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, * linkend="gdbus-unix-fd-client"/> for an example of how to use this * low-level API to send and receive UNIX file descriptors. * + * Note that @message must be unlocked, unless @flags contain the + * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag. + * * Returns: %TRUE if the message was well-formed and queued for * transmission, %FALSE if @error is set. * @@ -1403,6 +1700,7 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, gboolean g_dbus_connection_send_message (GDBusConnection *connection, GDBusMessage *message, + GDBusSendMessageFlags flags, volatile guint32 *out_serial, GError **error) { @@ -1410,10 +1708,11 @@ g_dbus_connection_send_message (GDBusConnection *connection, g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), FALSE); + g_return_val_if_fail ((flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) || !g_dbus_message_get_locked (message), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); CONNECTION_LOCK (connection); - ret = g_dbus_connection_send_message_unlocked (connection, message, out_serial, error); + ret = g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, error); CONNECTION_UNLOCK (connection); return ret; } @@ -1438,6 +1737,7 @@ typedef struct gboolean delivered; } SendMessageData; +/* Can be called from any thread with or without lock held */ static SendMessageData * send_message_data_ref (SendMessageData *data) { @@ -1445,6 +1745,7 @@ send_message_data_ref (SendMessageData *data) return data; } +/* Can be called from any thread with or without lock held */ static void send_message_data_unref (SendMessageData *data) { @@ -1456,8 +1757,7 @@ send_message_data_unref (SendMessageData *data) g_object_unref (data->connection); if (data->cancellable != NULL) g_object_unref (data->cancellable); - if (data->main_context != NULL) - g_main_context_unref (data->main_context); + g_main_context_unref (data->main_context); g_free (data); } } @@ -1466,7 +1766,7 @@ send_message_data_unref (SendMessageData *data) /* can be called from any thread with lock held - caller must have prepared GSimpleAsyncResult already */ static void -send_message_with_reply_deliver (SendMessageData *data) +send_message_with_reply_deliver (SendMessageData *data, gboolean remove) { CONNECTION_ENSURE_LOCK (data->connection); @@ -1489,15 +1789,18 @@ send_message_with_reply_deliver (SendMessageData *data) data->cancellable_handler_id = 0; } - g_warn_if_fail (g_hash_table_remove (data->connection->map_method_serial_to_send_message_data, - GUINT_TO_POINTER (data->serial))); + if (remove) + { + g_warn_if_fail (g_hash_table_remove (data->connection->map_method_serial_to_send_message_data, + GUINT_TO_POINTER (data->serial))); + } send_message_data_unref (data); } /* ---------------------------------------------------------------------------------------------------- */ -/* must hold lock */ +/* Can be called from any thread with lock held */ static void send_message_data_deliver_reply_unlocked (SendMessageData *data, GDBusMessage *reply) @@ -1509,7 +1812,7 @@ send_message_data_deliver_reply_unlocked (SendMessageData *data, g_object_ref (reply), g_object_unref); - send_message_with_reply_deliver (data); + send_message_with_reply_deliver (data, TRUE); out: ; @@ -1517,6 +1820,7 @@ send_message_data_deliver_reply_unlocked (SendMessageData *data, /* ---------------------------------------------------------------------------------------------------- */ +/* Called from a user thread, lock is not held */ static gboolean send_message_with_reply_cancelled_idle_cb (gpointer user_data) { @@ -1531,7 +1835,7 @@ send_message_with_reply_cancelled_idle_cb (gpointer user_data) G_IO_ERROR_CANCELLED, _("Operation was cancelled")); - send_message_with_reply_deliver (data); + send_message_with_reply_deliver (data, TRUE); out: CONNECTION_UNLOCK (data->connection); @@ -1561,6 +1865,7 @@ send_message_with_reply_cancelled_cb (GCancellable *cancellable, /* ---------------------------------------------------------------------------------------------------- */ +/* Called from a user thread, lock is not held */ static gboolean send_message_with_reply_timeout_cb (gpointer user_data) { @@ -1575,7 +1880,7 @@ send_message_with_reply_timeout_cb (gpointer user_data) G_IO_ERROR_TIMED_OUT, _("Timeout was reached")); - send_message_with_reply_deliver (data); + send_message_with_reply_deliver (data, TRUE); out: CONNECTION_UNLOCK (data->connection); @@ -1585,9 +1890,11 @@ send_message_with_reply_timeout_cb (gpointer user_data) /* ---------------------------------------------------------------------------------------------------- */ +/* Called from a user thread, connection's lock is held */ static void g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connection, GDBusMessage *message, + GDBusSendMessageFlags flags, gint timeout_msec, volatile guint32 *out_serial, GCancellable *cancellable, @@ -1611,6 +1918,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect callback, user_data, g_dbus_connection_send_message_with_reply); + g_simple_async_result_set_check_cancellable (simple, cancellable); if (g_cancellable_is_cancelled (cancellable)) { @@ -1623,21 +1931,10 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect goto out; } - if (connection->closed) - { - g_simple_async_result_set_error (simple, - G_IO_ERROR, - G_IO_ERROR_CLOSED, - _("The connection is closed")); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - goto out; - } - error = NULL; - if (!g_dbus_connection_send_message_unlocked (connection, message, out_serial, &error)) + if (!g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, &error)) { - g_simple_async_result_set_from_error (simple, error); + g_simple_async_result_take_error (simple, error); g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); goto out; @@ -1648,9 +1945,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect data->connection = g_object_ref (connection); data->simple = simple; data->serial = *out_serial; - data->main_context = g_main_context_get_thread_default (); - if (data->main_context != NULL) - g_main_context_ref (data->main_context); + data->main_context = g_main_context_ref_thread_default (); if (cancellable != NULL) { @@ -1665,14 +1960,17 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect (GDestroyNotify) g_object_unref); } - data->timeout_source = g_timeout_source_new (timeout_msec); - g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT); - g_source_set_callback (data->timeout_source, - send_message_with_reply_timeout_cb, - send_message_data_ref (data), - (GDestroyNotify) send_message_data_unref); - g_source_attach (data->timeout_source, data->main_context); - g_source_unref (data->timeout_source); + if (timeout_msec != G_MAXINT) + { + data->timeout_source = g_timeout_source_new (timeout_msec); + g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT); + g_source_set_callback (data->timeout_source, + send_message_with_reply_timeout_cb, + send_message_data_ref (data), + (GDestroyNotify) send_message_data_unref); + g_source_attach (data->timeout_source, data->main_context); + g_source_unref (data->timeout_source); + } g_hash_table_insert (connection->map_method_serial_to_send_message_data, GUINT_TO_POINTER (*out_serial), @@ -1686,20 +1984,23 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect * g_dbus_connection_send_message_with_reply: * @connection: A #GDBusConnection. * @message: A #GDBusMessage. - * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. - * @out_serial: Return location for serial number assigned to @message when sending it or %NULL. + * @flags: Flags affecting how the message is sent. + * @timeout_msec: The timeout in milliseconds, -1 to use the default + * timeout or %G_MAXINT for no timeout. + * @out_serial: (out) (allow-none): Return location for serial number assigned + * to @message when sending it or %NULL. * @cancellable: A #GCancellable or %NULL. - * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't - * care about the result. + * @callback: (allow-none): A #GAsyncReadyCallback to call when the request is + * satisfied or %NULL if you don't care about the result. * @user_data: The data to pass to @callback. * * Asynchronously sends @message to the peer represented by @connection. * - * If g_dbus_message_get_serial() returns non-zero for @message, then - * that value is used for the message serial number. Otherwise a - * serial number will be assigned by @connection and set on @message - * via g_dbus_message_set_serial(). If @out_serial is not %NULL, then - * the serial number used will be written to this location prior to + * Unless @flags contain the + * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag, the serial number + * will be assigned by @connection and set on @message via + * g_dbus_message_set_serial(). If @out_serial is not %NULL, then the + * serial number used will be written to this location prior to * submitting the message to the underlying transport. * * If @connection is closed then the operation will fail with @@ -1713,6 +2014,9 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect * g_dbus_connection_send_message_with_reply_finish() to get the result of the operation. * See g_dbus_connection_send_message_with_reply_sync() for the synchronous version. * + * Note that @message must be unlocked, unless @flags contain the + * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag. + * * See and for an example of how to use this * low-level API to send and receive UNIX file descriptors. @@ -1722,6 +2026,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect void g_dbus_connection_send_message_with_reply (GDBusConnection *connection, GDBusMessage *message, + GDBusSendMessageFlags flags, gint timeout_msec, volatile guint32 *out_serial, GCancellable *cancellable, @@ -1730,11 +2035,13 @@ g_dbus_connection_send_message_with_reply (GDBusConnection *connection, { g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); g_return_if_fail (G_IS_DBUS_MESSAGE (message)); + g_return_if_fail ((flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) || !g_dbus_message_get_locked (message)); g_return_if_fail (timeout_msec >= 0 || timeout_msec == -1); CONNECTION_LOCK (connection); g_dbus_connection_send_message_with_reply_unlocked (connection, message, + flags, timeout_msec, out_serial, cancellable, @@ -1752,7 +2059,7 @@ g_dbus_connection_send_message_with_reply (GDBusConnection *connection, * Finishes an operation started with g_dbus_connection_send_message_with_reply(). * * Note that @error is only set if a local in-process error - * occured. That is to say that the returned #GDBusMessage object may + * occurred. That is to say that the returned #GDBusMessage object may * be of type %G_DBUS_MESSAGE_TYPE_ERROR. Use * g_dbus_message_to_gerror() to transcode this to a #GError. * @@ -1760,7 +2067,7 @@ g_dbus_connection_send_message_with_reply (GDBusConnection *connection, * linkend="gdbus-unix-fd-client"/> for an example of how to use this * low-level API to send and receive UNIX file descriptors. * - * Returns: A #GDBusMessage or %NULL if @error is set. + * Returns: (transfer full): A locked #GDBusMessage or %NULL if @error is set. * * Since: 2.26 */ @@ -1807,6 +2114,7 @@ typedef struct GMainLoop *loop; } SendMessageSyncData; +/* Called from a user thread, lock is not held */ static void send_message_with_reply_sync_cb (GDBusConnection *connection, GAsyncResult *res, @@ -1821,8 +2129,11 @@ send_message_with_reply_sync_cb (GDBusConnection *connection, * g_dbus_connection_send_message_with_reply_sync: * @connection: A #GDBusConnection. * @message: A #GDBusMessage. - * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. - * @out_serial: Return location for serial number assigned to @message when sending it or %NULL. + * @flags: Flags affecting how the message is sent. + * @timeout_msec: The timeout in milliseconds, -1 to use the default + * timeout or %G_MAXINT for no timeout. + * @out_serial: (out) (allow-none): Return location for serial number assigned + * to @message when sending it or %NULL. * @cancellable: A #GCancellable or %NULL. * @error: Return location for error or %NULL. * @@ -1831,11 +2142,11 @@ send_message_with_reply_sync_cb (GDBusConnection *connection, * timeout is reached. See g_dbus_connection_send_message_with_reply() * for the asynchronous version of this method. * - * If g_dbus_message_get_serial() returns non-zero for @message, then - * that value is used for the message serial number. Otherwise a - * serial number will be assigned by @connection and set on @message - * via g_dbus_message_set_serial(). If @out_serial is not %NULL, then - * the serial number used will be written to this location prior to + * Unless @flags contain the + * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag, the serial number + * will be assigned by @connection and set on @message via + * g_dbus_message_set_serial(). If @out_serial is not %NULL, then the + * serial number used will be written to this location prior to * submitting the message to the underlying transport. * * If @connection is closed then the operation will fail with @@ -1844,7 +2155,7 @@ send_message_with_reply_sync_cb (GDBusConnection *connection, * the operation fails with %G_IO_ERROR_INVALID_ARGUMENT. * * Note that @error is only set if a local in-process error - * occured. That is to say that the returned #GDBusMessage object may + * occurred. That is to say that the returned #GDBusMessage object may * be of type %G_DBUS_MESSAGE_TYPE_ERROR. Use * g_dbus_message_to_gerror() to transcode this to a #GError. * @@ -1852,13 +2163,17 @@ send_message_with_reply_sync_cb (GDBusConnection *connection, * linkend="gdbus-unix-fd-client"/> for an example of how to use this * low-level API to send and receive UNIX file descriptors. * - * Returns: A #GDBusMessage that is the reply to @message or %NULL if @error is set. + * Note that @message must be unlocked, unless @flags contain the + * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag. + * + * Returns: (transfer full): A locked #GDBusMessage that is the reply to @message or %NULL if @error is set. * * Since: 2.26 */ GDBusMessage * g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, GDBusMessage *message, + GDBusSendMessageFlags flags, gint timeout_msec, volatile guint32 *out_serial, GCancellable *cancellable, @@ -1869,6 +2184,7 @@ g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL); + g_return_val_if_fail ((flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) || !g_dbus_message_get_locked (message), NULL); g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); @@ -1880,6 +2196,7 @@ g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, g_dbus_connection_send_message_with_reply (connection, message, + flags, timeout_msec, out_serial, cancellable, @@ -1916,21 +2233,35 @@ typedef struct GDestroyNotify user_data_free_func; } FilterData; -/* Called in worker's thread - we must not block */ +/* Called in GDBusWorker's thread - we must not block - with no lock held */ static void on_worker_message_received (GDBusWorker *worker, GDBusMessage *message, gpointer user_data) { - GDBusConnection *connection = G_DBUS_CONNECTION (user_data); + GDBusConnection *connection; FilterCallback *filters; - gboolean consumed_by_filter; guint num_filters; guint n; + gboolean alive; + + G_LOCK (message_bus_lock); + alive = (g_hash_table_lookup (alive_connections, user_data) != NULL); + if (!alive) + { + G_UNLOCK (message_bus_lock); + return; + } + connection = G_DBUS_CONNECTION (user_data); + g_object_ref (connection); + G_UNLOCK (message_bus_lock); //g_debug ("in on_worker_message_received"); - g_object_ref (connection); + g_object_ref (message); + g_dbus_message_lock (message); + + //g_debug ("boo ref_count = %d %p %p", G_OBJECT (connection)->ref_count, connection, connection->worker); /* First collect the set of callback functions */ CONNECTION_LOCK (connection); @@ -1944,20 +2275,22 @@ on_worker_message_received (GDBusWorker *worker, } CONNECTION_UNLOCK (connection); - /* the call the filters in order (without holding the lock) */ - consumed_by_filter = FALSE; + /* then call the filters in order (without holding the lock) */ for (n = 0; n < num_filters; n++) { - consumed_by_filter = filters[n].func (connection, - message, - TRUE, - filters[n].user_data); - if (consumed_by_filter) + message = filters[n].func (connection, + message, + TRUE, + filters[n].user_data); + if (message == NULL) break; + g_dbus_message_lock (message); } - /* Standard dispatch unless the filter ate the message */ - if (!consumed_by_filter) + /* Standard dispatch unless the filter ate the message - no need to + * do anything if the message was altered + */ + if (message != NULL) { GDBusMessageType message_type; @@ -1996,25 +2329,36 @@ on_worker_message_received (GDBusWorker *worker, } } + if (message != NULL) + g_object_unref (message); g_object_unref (connection); g_free (filters); } -/* Called in worker's thread */ -static gboolean +/* Called in GDBusWorker's thread, lock is not held */ +static GDBusMessage * on_worker_message_about_to_be_sent (GDBusWorker *worker, GDBusMessage *message, gpointer user_data) { - GDBusConnection *connection = G_DBUS_CONNECTION (user_data); + GDBusConnection *connection; FilterCallback *filters; - gboolean consumed_by_filter; guint num_filters; guint n; + gboolean alive; - //g_debug ("in on_worker_message_about_to_be_sent"); - + G_LOCK (message_bus_lock); + alive = (g_hash_table_lookup (alive_connections, user_data) != NULL); + if (!alive) + { + G_UNLOCK (message_bus_lock); + return message; + } + connection = G_DBUS_CONNECTION (user_data); g_object_ref (connection); + G_UNLOCK (message_bus_lock); + + //g_debug ("in on_worker_message_about_to_be_sent"); /* First collect the set of callback functions */ CONNECTION_LOCK (connection); @@ -2028,44 +2372,94 @@ on_worker_message_about_to_be_sent (GDBusWorker *worker, } CONNECTION_UNLOCK (connection); - /* the call the filters in order (without holding the lock) */ - consumed_by_filter = FALSE; + /* then call the filters in order (without holding the lock) */ for (n = 0; n < num_filters; n++) { - consumed_by_filter = filters[n].func (connection, - message, - FALSE, - filters[n].user_data); - if (consumed_by_filter) + g_dbus_message_lock (message); + message = filters[n].func (connection, + message, + FALSE, + filters[n].user_data); + if (message == NULL) break; } g_object_unref (connection); g_free (filters); - return consumed_by_filter; + return message; +} + +/* called with connection lock held, in GDBusWorker thread */ +static gboolean +cancel_method_on_close (gpointer key, gpointer value, gpointer user_data) +{ + SendMessageData *data = value; + + if (data->delivered) + return FALSE; + + g_simple_async_result_set_error (data->simple, + G_IO_ERROR, + G_IO_ERROR_CLOSED, + _("The connection is closed")); + + /* Ask send_message_with_reply_deliver not to remove the element from the + * hash table - we're in the middle of a foreach; that would be unsafe. + * Instead, return TRUE from this function so that it gets removed safely. + */ + send_message_with_reply_deliver (data, FALSE); + return TRUE; } -/* Called in worker's thread - we must not block */ +/* Called in GDBusWorker's thread - we must not block - without lock held */ static void on_worker_closed (GDBusWorker *worker, gboolean remote_peer_vanished, GError *error, gpointer user_data) { - GDBusConnection *connection = G_DBUS_CONNECTION (user_data); + GDBusConnection *connection; + gboolean alive; + guint old_atomic_flags; + + G_LOCK (message_bus_lock); + alive = (g_hash_table_lookup (alive_connections, user_data) != NULL); + if (!alive) + { + G_UNLOCK (message_bus_lock); + return; + } + connection = G_DBUS_CONNECTION (user_data); + g_object_ref (connection); + G_UNLOCK (message_bus_lock); //g_debug ("in on_worker_closed: %s", error->message); CONNECTION_LOCK (connection); - if (!connection->closed) - set_closed_unlocked (connection, remote_peer_vanished, error); + /* Even though this is atomic, we do it inside the lock to avoid breaking + * assumptions in remove_match_rule(). We'd need the lock in a moment + * anyway, so, no loss. + */ + old_atomic_flags = g_atomic_int_or (&connection->atomic_flags, FLAG_CLOSED); + + if (!(old_atomic_flags & FLAG_CLOSED)) + { + g_hash_table_foreach_remove (connection->map_method_serial_to_send_message_data, cancel_method_on_close, NULL); + schedule_closed_unlocked (connection, remote_peer_vanished, error); + } CONNECTION_UNLOCK (connection); + + g_object_unref (connection); } /* ---------------------------------------------------------------------------------------------------- */ -/* Determines the biggest set of capabilities we can support on this connection */ +/* Determines the biggest set of capabilities we can support on this + * connection. + * + * Called with the init_lock held. + */ static GDBusCapabilityFlags get_offered_capabilities_max (GDBusConnection *connection) { @@ -2078,6 +2472,7 @@ get_offered_capabilities_max (GDBusConnection *connection) return ret; } +/* Called in a user thread, lock is not held */ static gboolean initable_init (GInitable *initable, GCancellable *cancellable, @@ -2094,18 +2489,20 @@ initable_init (GInitable *initable, * callbacks above needs the lock during initialization (for message * bus connections we do a synchronous Hello() call on the bus). */ - g_mutex_lock (connection->init_lock); + g_mutex_lock (&connection->init_lock); ret = FALSE; - if (connection->is_initialized) + /* Make this a no-op if we're already initialized (successfully or + * unsuccessfully) + */ + if ((g_atomic_int_get (&connection->atomic_flags) & FLAG_INITIALIZED)) { - if (connection->stream != NULL) - ret = TRUE; - else - g_assert (connection->initialization_error != NULL); + ret = (connection->initialization_error == NULL); goto out; } + + /* Because of init_lock, we can't get here twice in different threads */ g_assert (connection->initialization_error == NULL); /* The user can pass multiple (but mutally exclusive) construct @@ -2124,7 +2521,7 @@ initable_init (GInitable *initable, if ((connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER) || (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS)) { - g_set_error_literal (error, + g_set_error_literal (&connection->initialization_error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, _("Unsupported flags encountered when constructing a client-side connection")); @@ -2159,7 +2556,7 @@ initable_init (GInitable *initable, (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS), get_offered_capabilities_max (connection), &connection->capabilities, - &connection->crendentials, + &connection->credentials, cancellable, &connection->initialization_error)) goto out; @@ -2189,11 +2586,8 @@ initable_init (GInitable *initable, //g_debug ("haz unix fd passing powers: %d", connection->capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); #ifdef G_OS_UNIX - /* Hack used until - * - * https://bugzilla.gnome.org/show_bug.cgi?id=616458 - * - * has been resolved + /* We want all IO operations to be non-blocking since they happen in + * the worker thread which is shared by _all_ connections. */ if (G_IS_SOCKET_CONNECTION (connection->stream)) { @@ -2201,9 +2595,15 @@ initable_init (GInitable *initable, } #endif + G_LOCK (message_bus_lock); + if (alive_connections == NULL) + alive_connections = g_hash_table_new (g_direct_hash, g_direct_equal); + g_hash_table_insert (alive_connections, connection, connection); + G_UNLOCK (message_bus_lock); + connection->worker = _g_dbus_worker_new (connection->stream, connection->capabilities, - (connection->flags & G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING), + ((connection->flags & G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING) != 0), on_worker_message_received, on_worker_message_about_to_be_sent, on_worker_closed, @@ -2231,7 +2631,7 @@ initable_init (GInitable *initable, "Hello", NULL, /* parameters */ G_VARIANT_TYPE ("(s)"), - G_DBUS_CALL_FLAGS_NONE, + CALL_FLAGS_INITIALIZING, -1, NULL, /* TODO: cancellable */ &connection->initialization_error); @@ -2243,8 +2643,6 @@ initable_init (GInitable *initable, //g_debug ("unique name is `%s'", connection->bus_unique_name); } - connection->is_initialized = TRUE; - ret = TRUE; out: if (!ret) @@ -2253,7 +2651,8 @@ initable_init (GInitable *initable, g_propagate_error (error, g_error_copy (connection->initialization_error)); } - g_mutex_unlock (connection->init_lock); + g_atomic_int_or (&connection->atomic_flags, FLAG_INITIALIZED); + g_mutex_unlock (&connection->init_lock); return ret; } @@ -2277,9 +2676,9 @@ async_initable_iface_init (GAsyncInitableIface *async_initable_iface) /** * g_dbus_connection_new: * @stream: A #GIOStream. - * @guid: The GUID to use if a authenticating as a server or %NULL. + * @guid: (allow-none): The GUID to use if a authenticating as a server or %NULL. * @flags: Flags describing how to make the connection. - * @observer: A #GDBusAuthObserver or %NULL. + * @observer: (allow-none): A #GDBusAuthObserver or %NULL. * @cancellable: A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. @@ -2287,6 +2686,13 @@ async_initable_iface_init (GAsyncInitableIface *async_initable_iface) * Asynchronously sets up a D-Bus connection for exchanging D-Bus messages * with the end represented by @stream. * + * If @stream is a #GSocketConnection, then the corresponding #GSocket + * will be put into non-blocking mode. + * + * The D-Bus connection will interact with @stream from a worker thread. + * As a result, the caller should not interact with @stream after this + * method has been called, except by calling g_object_unref() on it. + * * If @observer is not %NULL it may be used to control the * authentication process. * @@ -2358,15 +2764,22 @@ g_dbus_connection_new_finish (GAsyncResult *res, /** * g_dbus_connection_new_sync: * @stream: A #GIOStream. - * @guid: The GUID to use if a authenticating as a server or %NULL. + * @guid: (allow-none): The GUID to use if a authenticating as a server or %NULL. * @flags: Flags describing how to make the connection. - * @observer: A #GDBusAuthObserver or %NULL. + * @observer: (allow-none): A #GDBusAuthObserver or %NULL. * @cancellable: A #GCancellable or %NULL. * @error: Return location for error or %NULL. * * Synchronously sets up a D-Bus connection for exchanging D-Bus messages * with the end represented by @stream. * + * If @stream is a #GSocketConnection, then the corresponding #GSocket + * will be put into non-blocking mode. + * + * The D-Bus connection will interact with @stream from a worker thread. + * As a result, the caller should not interact with @stream after this + * method has been called, except by calling g_object_unref() on it. + * * If @observer is not %NULL it may be used to control the * authentication process. * @@ -2403,7 +2816,7 @@ g_dbus_connection_new_sync (GIOStream *stream, * g_dbus_connection_new_for_address: * @address: A D-Bus address. * @flags: Flags describing how to make the connection. - * @observer: A #GDBusAuthObserver or %NULL. + * @observer: (allow-none): A #GDBusAuthObserver or %NULL. * @cancellable: A #GCancellable or %NULL. * @callback: A #GAsyncReadyCallback to call when the request is satisfied. * @user_data: The data to pass to @callback. @@ -2488,7 +2901,7 @@ g_dbus_connection_new_for_address_finish (GAsyncResult *res, * g_dbus_connection_new_for_address_sync: * @address: A D-Bus address. * @flags: Flags describing how to make the connection. - * @observer: A #GDBusAuthObserver or %NULL. + * @observer: (allow-none): A #GDBusAuthObserver or %NULL. * @cancellable: A #GCancellable or %NULL. * @error: Return location for error or %NULL. * @@ -2536,12 +2949,19 @@ g_dbus_connection_new_for_address_sync (const gchar *address, * g_dbus_connection_set_exit_on_close: * @connection: A #GDBusConnection. * @exit_on_close: Whether the process should be terminated - * when @connection is closed by the remote peer. + * when @connection is closed by the remote peer. * * Sets whether the process should be terminated when @connection is * closed by the remote peer. See #GDBusConnection:exit-on-close for * more details. * + * Note that this function should be used with care. Most modern UNIX + * desktops tie the notion of a user session the session bus, and expect + * all of a users applications to quit when their bus connection goes away. + * If you are setting @exit_on_close to %FALSE for the shared session + * bus connection, you should make sure that your application exits + * when the user session ends. + * * Since: 2.26 */ void @@ -2549,7 +2969,12 @@ g_dbus_connection_set_exit_on_close (GDBusConnection *connection, gboolean exit_on_close) { g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); - connection->exit_on_close = exit_on_close; + + if (exit_on_close) + g_atomic_int_or (&connection->atomic_flags, FLAG_EXIT_ON_CLOSE); + else + g_atomic_int_and (&connection->atomic_flags, ~FLAG_EXIT_ON_CLOSE); + } /** @@ -2569,7 +2994,11 @@ gboolean g_dbus_connection_get_exit_on_close (GDBusConnection *connection) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); - return connection->exit_on_close; + + if (g_atomic_int_get (&connection->atomic_flags) & FLAG_EXIT_ON_CLOSE) + return TRUE; + else + return FALSE; } /** @@ -2609,6 +3038,11 @@ const gchar * g_dbus_connection_get_unique_name (GDBusConnection *connection) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + + /* do not use g_return_val_if_fail(), we want the memory barrier */ + if (!check_initialized (connection)) + return NULL; + return connection->bus_unique_name; } @@ -2626,7 +3060,7 @@ g_dbus_connection_get_unique_name (GDBusConnection *connection) * each application is a client. So this method will always return * %NULL for message bus clients. * - * Returns: A #GCredentials or %NULL if not available. Do not free + * Returns: (transfer none): A #GCredentials or %NULL if not available. Do not free * this object, it is owned by @connection. * * Since: 2.26 @@ -2635,7 +3069,12 @@ GCredentials * g_dbus_connection_get_peer_credentials (GDBusConnection *connection) { g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); - return connection->crendentials; + + /* do not use g_return_val_if_fail(), we want the memory barrier */ + if (!check_initialized (connection)) + return NULL; + + return connection->credentials; } /* ---------------------------------------------------------------------------------------------------- */ @@ -2655,19 +3094,18 @@ static guint _global_filter_id = 1; * are run in the order that they were added. The same handler can be * added as a filter more than once, in which case it will be run more * than once. Filters added during a filter callback won't be run on - * the message being processed. + * the message being processed. Filter functions are allowed to modify + * and even drop messages. * * Note that filters are run in a dedicated message handling thread so * they can't block and, generally, can't do anything but signal a * worker thread. Also note that filters are rarely needed - use API * such as g_dbus_connection_send_message_with_reply(), - * g_dbus_connection_signal_subscribe() or - * g_dbus_connection_call() instead. + * g_dbus_connection_signal_subscribe() or g_dbus_connection_call() instead. * - * If a filter consumes an incoming message (by returning %TRUE), the - * message is not dispatched anywhere else - not even the standard - * dispatch machinery (that API such as - * g_dbus_connection_signal_subscribe() and + * If a filter consumes an incoming message the message is not + * dispatched anywhere else - not even the standard dispatch machinery + * (that API such as g_dbus_connection_signal_subscribe() and * g_dbus_connection_send_message_with_reply() relies on) will see the * message. Similary, if a filter consumes an outgoing message, the * message will not be sent to the other peer. @@ -2687,6 +3125,7 @@ g_dbus_connection_add_filter (GDBusConnection *connection, g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); g_return_val_if_fail (filter_function != NULL, 0); + g_return_val_if_fail (check_initialized (connection), 0); CONNECTION_LOCK (connection); data = g_new0 (FilterData, 1); @@ -2731,6 +3170,7 @@ g_dbus_connection_remove_filter (GDBusConnection *connection, FilterData *to_destroy; g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + g_return_if_fail (check_initialized (connection)); CONNECTION_LOCK (connection); to_destroy = NULL; @@ -2801,11 +3241,14 @@ args_to_rule (const gchar *sender, const gchar *interface_name, const gchar *member, const gchar *object_path, - const gchar *arg0) + const gchar *arg0, + gboolean negate) { GString *rule; rule = g_string_new ("type='signal'"); + if (negate) + g_string_prepend_c (rule, '-'); if (sender != NULL) g_string_append_printf (rule, ",sender='%s'", sender); if (interface_name != NULL) @@ -2826,7 +3269,7 @@ static guint _global_subtree_registration_id = 1; /* ---------------------------------------------------------------------------------------------------- */ -/* must hold lock when calling */ +/* Called in a user thread, lock is held */ static void add_match_rule (GDBusConnection *connection, const gchar *match_rule) @@ -2834,6 +3277,9 @@ add_match_rule (GDBusConnection *connection, GError *error; GDBusMessage *message; + if (match_rule[0] == '-') + return; + message = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */ "/org/freedesktop/DBus", /* path */ "org.freedesktop.DBus", /* interface */ @@ -2842,6 +3288,7 @@ add_match_rule (GDBusConnection *connection, error = NULL; if (!g_dbus_connection_send_message_unlocked (connection, message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, &error)) { @@ -2853,7 +3300,7 @@ add_match_rule (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ -/* must hold lock when calling */ +/* Called in a user thread, lock is held */ static void remove_match_rule (GDBusConnection *connection, const gchar *match_rule) @@ -2861,6 +3308,9 @@ remove_match_rule (GDBusConnection *connection, GError *error; GDBusMessage *message; + if (match_rule[0] == '-') + return; + message = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */ "/org/freedesktop/DBus", /* path */ "org.freedesktop.DBus", /* interface */ @@ -2870,9 +3320,14 @@ remove_match_rule (GDBusConnection *connection, error = NULL; if (!g_dbus_connection_send_message_unlocked (connection, message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, &error)) { + /* If we could get G_IO_ERROR_CLOSED here, it wouldn't be reasonable to + * critical; but we're holding the lock, and our caller checked whether + * we were already closed, so we can't get that error. + */ g_critical ("Error while sending RemoveMatch() message: %s", error->message); g_error_free (error); } @@ -2896,15 +3351,19 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) /** * g_dbus_connection_signal_subscribe: * @connection: A #GDBusConnection. - * @sender: Sender name to match on (unique or well-known name) or %NULL to listen from all senders. - * @interface_name: D-Bus interface name to match on or %NULL to match on all interfaces. - * @member: D-Bus signal name to match on or %NULL to match on all signals. - * @object_path: Object path to match on or %NULL to match on all object paths. - * @arg0: Contents of first string argument to match on or %NULL to match on all kinds of arguments. + * @sender: (allow-none): Sender name to match on (unique or well-known name) + * or %NULL to listen from all senders. + * @interface_name: (allow-none): D-Bus interface name to match on or %NULL to + * match on all interfaces. + * @member: (allow-none): D-Bus signal name to match on or %NULL to match on all signals. + * @object_path: (allow-none): Object path to match on or %NULL to match on all object paths. + * @arg0: (allow-none): Contents of first string argument to match on or %NULL + * to match on all kinds of arguments. * @flags: Flags describing how to subscribe to the signal (currently unused). * @callback: Callback to invoke when there is a signal matching the requested data. * @user_data: User data to pass to @callback. - * @user_data_free_func: Function to free @user_data with when subscription is removed or %NULL. + * @user_data_free_func: (allow-none): Function to free @user_data with when + * subscription is removed or %NULL. * * Subscribes to signals on @connection and invokes @callback with a * whenever the signal is received. Note that @callback @@ -2960,10 +3419,20 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, g_return_val_if_fail (member == NULL || g_dbus_is_member_name (member), 0); g_return_val_if_fail (object_path == NULL || g_variant_is_object_path (object_path), 0); g_return_val_if_fail (callback != NULL, 0); + g_return_val_if_fail (check_initialized (connection), 0); CONNECTION_LOCK (connection); - rule = args_to_rule (sender, interface_name, member, object_path, arg0); + /* If G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE was specified, we will end up + * with a '-' character to prefix the rule (which will otherwise be + * normal). + * + * This allows us to hash the rule and do our lifecycle tracking in + * the usual way, but the '-' prevents the match rule from ever + * actually being send to the bus (either for add or remove). + */ + rule = args_to_rule (sender, interface_name, member, object_path, arg0, + (flags & G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE) != 0); if (sender != NULL && (g_dbus_is_unique_name (sender) || g_strcmp0 (sender, "org.freedesktop.DBus") == 0)) sender_unique_name = sender; @@ -2974,9 +3443,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, subscriber.user_data = user_data; subscriber.user_data_free_func = user_data_free_func; subscriber.id = _global_subscriber_id++; /* TODO: overflow etc. */ - subscriber.context = g_main_context_get_thread_default (); - if (subscriber.context != NULL) - g_main_context_ref (subscriber.context); + 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); @@ -3036,7 +3503,8 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ -/* must hold lock when calling this */ +/* called in any thread */ +/* must hold lock when calling this (except if connection->finalizing is TRUE) */ static void unsubscribe_id_internal (GDBusConnection *connection, guint subscription_id, @@ -3083,12 +3551,20 @@ unsubscribe_id_internal (GDBusConnection *connection, } /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */ - if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) + if ((connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) && + !is_signal_data_for_name_lost_or_acquired (signal_data) && + !g_dbus_connection_is_closed (connection) && + !connection->finalizing) { - if (!is_signal_data_for_name_lost_or_acquired (signal_data)) - if (!connection->closed) - remove_match_rule (connection, signal_data->rule); + /* The check for g_dbus_connection_is_closed() means that + * sending the RemoveMatch message can't fail with + * G_IO_ERROR_CLOSED, because we're holding the lock, + * so on_worker_closed() can't happen between the check we just + * did, and releasing the lock later. + */ + remove_match_rule (connection, signal_data->rule); } + signal_data_free (signal_data); } @@ -3118,6 +3594,7 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, 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)); @@ -3135,10 +3612,10 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, { SignalSubscriber *subscriber; subscriber = &(g_array_index (subscribers, SignalSubscriber, n)); - if (subscriber->user_data_free_func != NULL) - subscriber->user_data_free_func (subscriber->user_data); - if (subscriber->context != NULL) - g_main_context_unref (subscriber->context); + call_destroy_notify (subscriber->context, + subscriber->user_data_free_func, + subscriber->user_data); + g_main_context_unref (subscriber->context); } g_array_free (subscribers, TRUE); @@ -3207,8 +3684,7 @@ emit_signal_instance_in_idle_cb (gpointer data) parameters, signal_instance->user_data); - if (parameters != NULL) - g_variant_unref (parameters); + g_variant_unref (parameters); return FALSE; } @@ -3221,7 +3697,7 @@ signal_instance_free (SignalInstance *signal_instance) g_free (signal_instance); } -/* called in message handler thread WITH lock held */ +/* called in GDBusWorker thread WITH lock held */ static void schedule_callbacks (GDBusConnection *connection, GPtrArray *signal_data_array, @@ -3308,7 +3784,7 @@ schedule_callbacks (GDBusConnection *connection, } } -/* called in message handler thread with lock held */ +/* called in GDBusWorker thread with lock held */ static void distribute_signals (GDBusConnection *connection, GDBusMessage *message) @@ -3323,7 +3799,7 @@ distribute_signals (GDBusConnection *connection, _g_dbus_debug_print_lock (); g_print ("========================================================================\n" "GDBus-debug:Signal:\n" - " >>>> SIGNAL %s.%s\n" + " <<<< RECEIVED SIGNAL %s.%s\n" " on object %s\n" " sent by name %s\n", g_dbus_message_get_interface (message), @@ -3382,10 +3858,10 @@ purge_all_signal_subscriptions (GDBusConnection *connection) { SignalSubscriber *subscriber; subscriber = &(g_array_index (subscribers, SignalSubscriber, n)); - if (subscriber->user_data_free_func != NULL) - subscriber->user_data_free_func (subscriber->user_data); - if (subscriber->context != NULL) - g_main_context_unref (subscriber->context); + call_destroy_notify (subscriber->context, + subscriber->user_data_free_func, + subscriber->user_data); + g_main_context_unref (subscriber->context); } g_array_free (subscribers, TRUE); @@ -3463,14 +3939,14 @@ typedef struct static void exported_interface_free (ExportedInterface *ei) { + g_dbus_interface_info_cache_release (ei->interface_info); g_dbus_interface_info_unref ((GDBusInterfaceInfo *) ei->interface_info); - if (ei->user_data_free_func != NULL) - /* TODO: push to thread-default mainloop */ - ei->user_data_free_func (ei->user_data); + call_destroy_notify (ei->context, + ei->user_data_free_func, + ei->user_data); - if (ei->context != NULL) - g_main_context_unref (ei->context); + g_main_context_unref (ei->context); g_free (ei->interface_name); _g_dbus_interface_vtable_free (ei->vtable); @@ -3483,7 +3959,7 @@ exported_interface_free (ExportedInterface *ei) * @subtree_registration_id (if not zero) has been unregistered. If * so, returns %TRUE. * - * Caller must *not* hold lock. + * May be called by any thread. Caller must *not* hold lock. */ static gboolean has_object_been_unregistered (GDBusConnection *connection, @@ -3552,7 +4028,7 @@ invoke_get_property_in_idle_cb (gpointer _data) "org.freedesktop.DBus.Error.UnknownMethod", _("No such interface `org.freedesktop.DBus.Properties' on object at path %s"), g_dbus_message_get_path (data->message)); - g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); goto out; } @@ -3571,10 +4047,10 @@ invoke_get_property_in_idle_cb (gpointer _data) { g_assert_no_error (error); - g_variant_ref_sink (value); + g_variant_take_ref (value); reply = g_dbus_message_new_method_reply (data->message); g_dbus_message_set_body (reply, g_variant_new ("(v)", value)); - g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_variant_unref (value); g_object_unref (reply); } @@ -3586,7 +4062,7 @@ invoke_get_property_in_idle_cb (gpointer _data) reply = g_dbus_message_new_method_error_literal (data->message, dbus_error_name, error->message); - g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_free (dbus_error_name); g_error_free (error); g_object_unref (reply); @@ -3653,13 +4129,14 @@ invoke_set_property_in_idle_cb (gpointer _data) out: g_assert (reply != NULL); - g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); + g_variant_unref (value); return FALSE; } -/* called with lock held */ +/* called in any thread with connection's lock held */ static gboolean validate_and_maybe_schedule_property_getset (GDBusConnection *connection, GDBusMessage *message, @@ -3717,7 +4194,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect "org.freedesktop.DBus.Error.InvalidArgs", _("No such property `%s'"), property_name); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -3729,7 +4206,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect "org.freedesktop.DBus.Error.InvalidArgs", _("Property `%s' is not readable"), property_name); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -3740,7 +4217,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect "org.freedesktop.DBus.Error.InvalidArgs", _("Property `%s' is not writable"), property_name); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -3773,7 +4250,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect return handled; } -/* called with lock held */ +/* called in GDBusWorker thread with connection's lock held */ static gboolean handle_getset_property (GDBusConnection *connection, ExportedObject *eo, @@ -3810,7 +4287,7 @@ handle_getset_property (GDBusConnection *connection, "org.freedesktop.DBus.Error.InvalidArgs", _("No such interface `%s'"), interface_name); - g_dbus_connection_send_message_unlocked (eo->connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -3856,7 +4333,6 @@ invoke_get_all_properties_in_idle_cb (gpointer _data) { PropertyGetAllData *data = _data; GVariantBuilder builder; - GError *error; GDBusMessage *reply; guint n; @@ -3868,13 +4344,11 @@ invoke_get_all_properties_in_idle_cb (gpointer _data) "org.freedesktop.DBus.Error.UnknownMethod", _("No such interface `org.freedesktop.DBus.Properties' on object at path %s"), g_dbus_message_get_path (data->message)); - g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); goto out; } - error = NULL; - /* TODO: Right now we never fail this call - we just omit values if * a get_property() call is failing. * @@ -3902,23 +4376,25 @@ invoke_get_all_properties_in_idle_cb (gpointer _data) if (value == NULL) continue; + g_variant_take_ref (value); g_variant_builder_add (&builder, "{sv}", property_info->name, value); + g_variant_unref (value); } g_variant_builder_close (&builder); reply = g_dbus_message_new_method_reply (data->message); g_dbus_message_set_body (reply, g_variant_builder_end (&builder)); - g_dbus_connection_send_message (data->connection, reply, NULL, NULL); + g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); out: return FALSE; } -/* called with lock held */ +/* called in any thread with connection's lock held */ static gboolean validate_and_maybe_schedule_property_get_all (GDBusConnection *connection, GDBusMessage *message, @@ -3968,7 +4444,7 @@ validate_and_maybe_schedule_property_get_all (GDBusConnection *connec return handled; } -/* called with lock held */ +/* called in GDBusWorker thread with connection's lock held */ static gboolean handle_get_all_properties (GDBusConnection *connection, ExportedObject *eo, @@ -3995,7 +4471,7 @@ handle_get_all_properties (GDBusConnection *connection, "org.freedesktop.DBus.Error.InvalidArgs", _("No such interface"), interface_name); - g_dbus_connection_send_message_unlocked (eo->connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -4024,7 +4500,7 @@ static const gchar introspect_header[] = static const gchar introspect_tail[] = "\n"; -static const gchar introspect_standard_interfaces[] = +static const gchar introspect_properties_interface[] = " \n" " \n" " \n" @@ -4045,7 +4521,9 @@ static const gchar introspect_standard_interfaces[] = " \n" " \n" " \n" - " \n" + " \n"; + +static const gchar introspect_introspectable_interface[] = " \n" " \n" " \n" @@ -4065,12 +4543,6 @@ introspect_append_header (GString *s) } static void -introspect_append_standard_interfaces (GString *s) -{ - g_string_append (s, introspect_standard_interfaces); -} - -static void maybe_add_path (const gchar *path, gsize path_len, const gchar *object_path, GHashTable *set) { if (g_str_has_prefix (object_path, path) && strlen (object_path) > path_len && object_path[path_len-1] == '/') @@ -4094,6 +4566,7 @@ maybe_add_path (const gchar *path, gsize path_len, const gchar *object_path, GHa } /* TODO: we want a nicer public interface for this */ +/* called in any thread with connection's lock held */ static gchar ** g_dbus_connection_list_registered_unlocked (GDBusConnection *connection, const gchar *path) @@ -4135,6 +4608,7 @@ g_dbus_connection_list_registered_unlocked (GDBusConnection *connection, return ret; } +/* called in any thread with connection's lock not held */ static gchar ** g_dbus_connection_list_registered (GDBusConnection *connection, const gchar *path) @@ -4146,7 +4620,7 @@ g_dbus_connection_list_registered (GDBusConnection *connection, return ret; } -/* called in message handler thread with lock held */ +/* called in GDBusWorker thread with connection's lock held */ static gboolean handle_introspect (GDBusConnection *connection, ExportedObject *eo, @@ -4161,10 +4635,17 @@ handle_introspect (GDBusConnection *connection, /* first the header with the standard interfaces */ s = g_string_sized_new (sizeof (introspect_header) + - sizeof (introspect_standard_interfaces) + + sizeof (introspect_properties_interface) + + sizeof (introspect_introspectable_interface) + sizeof (introspect_tail)); introspect_append_header (s); - introspect_append_standard_interfaces (s); + if (!g_hash_table_lookup (eo->map_if_name_to_ei, + "org.freedesktop.DBus.Properties")) + g_string_append (s, introspect_properties_interface); + + if (!g_hash_table_lookup (eo->map_if_name_to_ei, + "org.freedesktop.DBus.Introspectable")) + g_string_append (s, introspect_introspectable_interface); /* then include the registered interfaces */ g_hash_table_iter_init (&hash_iter, eo->map_if_name_to_ei); @@ -4180,7 +4661,7 @@ handle_introspect (GDBusConnection *connection, reply = g_dbus_message_new_method_reply (message); g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); g_string_free (s, TRUE); @@ -4212,7 +4693,7 @@ call_in_idle_cb (gpointer user_data) _("No such interface `%s' on object at path %s"), g_dbus_method_invocation_get_interface_name (invocation), g_dbus_method_invocation_get_object_path (invocation)); - g_dbus_connection_send_message (g_dbus_method_invocation_get_connection (invocation), reply, NULL, NULL); + g_dbus_connection_send_message (g_dbus_method_invocation_get_connection (invocation), reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); goto out; } @@ -4230,7 +4711,7 @@ call_in_idle_cb (gpointer user_data) return FALSE; } -/* called in message handler thread with lock held */ +/* called in GDBusWorker thread with connection's lock held */ static gboolean validate_and_maybe_schedule_method_call (GDBusConnection *connection, GDBusMessage *message, @@ -4263,7 +4744,7 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection, "org.freedesktop.DBus.Error.UnknownMethod", _("No such method `%s'"), g_dbus_message_get_member (message)); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -4295,7 +4776,7 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection, _("Type of message, `%s', does not match expected type `%s'"), g_variant_get_type_string (parameters), type_string); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_variant_type_free (in_type); g_variant_unref (parameters); g_object_unref (reply); @@ -4306,15 +4787,15 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection, g_variant_type_free (in_type); /* schedule the call in idle */ - invocation = g_dbus_method_invocation_new (g_dbus_message_get_sender (message), - g_dbus_message_get_path (message), - g_dbus_message_get_interface (message), - g_dbus_message_get_member (message), - method_info, - connection, - message, - parameters, - user_data); + invocation = _g_dbus_method_invocation_new (g_dbus_message_get_sender (message), + g_dbus_message_get_path (message), + g_dbus_message_get_interface (message), + g_dbus_message_get_member (message), + method_info, + connection, + message, + parameters, + user_data); g_variant_unref (parameters); /* TODO: would be nicer with a real MethodData like we already @@ -4340,7 +4821,7 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ -/* called in message handler thread with lock held */ +/* called in GDBusWorker thread with connection's lock held */ static gboolean obj_message_func (GDBusConnection *connection, ExportedObject *eo, @@ -4420,8 +4901,8 @@ obj_message_func (GDBusConnection *connection, * @connection: A #GDBusConnection. * @object_path: The object path to register at. * @interface_info: Introspection data for the interface. - * @vtable: A #GDBusInterfaceVTable to call into or %NULL. - * @user_data: Data to pass to functions in @vtable. + * @vtable: (allow-none): A #GDBusInterfaceVTable to call into or %NULL. + * @user_data: (allow-none): Data to pass to functions in @vtable. * @user_data_free_func: Function to call when the object path is unregistered. * @error: Return location for error or %NULL. * @@ -4487,6 +4968,7 @@ g_dbus_connection_register_object (GDBusConnection *connection, g_return_val_if_fail (interface_info != NULL, 0); g_return_val_if_fail (g_dbus_is_interface_name (interface_info->name), 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); + g_return_val_if_fail (check_initialized (connection), 0); ret = 0; @@ -4524,10 +5006,9 @@ g_dbus_connection_register_object (GDBusConnection *connection, ei->user_data_free_func = user_data_free_func; ei->vtable = _g_dbus_interface_vtable_copy (vtable); ei->interface_info = g_dbus_interface_info_ref (interface_info); + g_dbus_interface_info_cache_build (ei->interface_info); ei->interface_name = g_strdup (interface_info->name); - ei->context = g_main_context_get_thread_default (); - if (ei->context != NULL) - g_main_context_ref (ei->context); + ei->context = g_main_context_ref_thread_default (); g_hash_table_insert (eo->map_if_name_to_ei, (gpointer) ei->interface_name, @@ -4564,6 +5045,7 @@ g_dbus_connection_unregister_object (GDBusConnection *connection, gboolean ret; g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + g_return_val_if_fail (check_initialized (connection), FALSE); ret = FALSE; @@ -4596,11 +5078,13 @@ g_dbus_connection_unregister_object (GDBusConnection *connection, /** * g_dbus_connection_emit_signal: * @connection: A #GDBusConnection. - * @destination_bus_name: The unique bus name for the destination for the signal or %NULL to emit to all listeners. + * @destination_bus_name: (allow-none): The unique bus name for the destination + * for the signal or %NULL to emit to all listeners. * @object_path: Path of remote object. * @interface_name: D-Bus interface to emit a signal on. * @signal_name: The name of the signal to emit. - * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters. + * @parameters: (allow-none): A #GVariant tuple with parameters for the signal + * or %NULL if not passing parameters. * @error: Return location for error or %NULL. * * Emits a signal. @@ -4634,6 +5118,7 @@ g_dbus_connection_emit_signal (GDBusConnection *connection, g_return_val_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name), FALSE); g_return_val_if_fail (signal_name != NULL && g_dbus_is_member_name (signal_name), FALSE); g_return_val_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), FALSE); + g_return_val_if_fail (check_initialized (connection), FALSE); if (G_UNLIKELY (_g_dbus_debug_emission ())) { @@ -4661,7 +5146,7 @@ g_dbus_connection_emit_signal (GDBusConnection *connection, if (parameters != NULL) g_dbus_message_set_body (message, parameters); - ret = g_dbus_connection_send_message (connection, message, NULL, error); + ret = g_dbus_connection_send_message (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, error); g_object_unref (message); return ret; @@ -4679,6 +5164,7 @@ static GVariant * decode_method_reply (GDBusMessage *reply, const gchar *method_name, const GVariantType *reply_type, + GUnixFDList **out_fd_list, GError **error) { GVariant *result; @@ -4712,6 +5198,18 @@ decode_method_reply (GDBusMessage *reply, g_free (type_string); result = NULL; } + +#ifdef G_OS_UNIX + if (result != NULL) + { + if (out_fd_list != NULL) + { + *out_fd_list = g_dbus_message_get_unix_fd_list (reply); + if (*out_fd_list != NULL) + g_object_ref (*out_fd_list); + } + } +#endif break; case G_DBUS_MESSAGE_TYPE_ERROR: @@ -4733,18 +5231,35 @@ typedef struct GVariantType *reply_type; gchar *method_name; /* for error message */ guint32 serial; + + GVariant *value; + GUnixFDList *fd_list; } CallState; static void +call_state_free (CallState *state) +{ + g_variant_type_free (state->reply_type); + g_free (state->method_name); + + if (state->value != NULL) + g_variant_unref (state->value); + if (state->fd_list != NULL) + g_object_unref (state->fd_list); + g_slice_free (CallState, state); +} + +/* called in any thread, with the connection's lock not held */ +static void g_dbus_connection_call_done (GObject *source, GAsyncResult *result, gpointer user_data) { + GSimpleAsyncResult *simple; GDBusConnection *connection = G_DBUS_CONNECTION (source); CallState *state = user_data; GError *error; GDBusMessage *reply; - GVariant *value; error = NULL; reply = g_dbus_connection_send_message_with_reply_finish (connection, @@ -4756,7 +5271,7 @@ g_dbus_connection_call_done (GObject *source, _g_dbus_debug_print_lock (); g_print ("========================================================================\n" "GDBus-debug:Call:\n" - " >>>> ASYNC COMPLETE %s() (serial %d)\n" + " <<<< ASYNC COMPLETE %s() (serial %d)\n" " ", state->method_name, state->serial); @@ -4772,47 +5287,289 @@ g_dbus_connection_call_done (GObject *source, _g_dbus_debug_print_unlock (); } - if (reply != NULL) + state->value = decode_method_reply (reply, state->method_name, state->reply_type, &state->fd_list, &error); + + simple = state->simple; /* why? because state is freed before we unref simple.. */ + if (error != NULL) { - value = decode_method_reply (reply, state->method_name, - state->reply_type, &error); - g_object_unref (reply); + g_simple_async_result_take_error (state->simple, error); + g_simple_async_result_complete (state->simple); + call_state_free (state); } else - value = NULL; + { + g_simple_async_result_set_op_res_gpointer (state->simple, state, (GDestroyNotify) call_state_free); + g_simple_async_result_complete (state->simple); + } + g_clear_object (&reply); + g_object_unref (simple); +} + +/* called in any thread, with the connection's lock not held */ +static void +g_dbus_connection_call_internal (GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + gint timeout_msec, + GUnixFDList *fd_list, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDBusMessage *message; + CallState *state; + + g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); + g_return_if_fail (bus_name == NULL || g_dbus_is_name (bus_name)); + g_return_if_fail (object_path != NULL && g_variant_is_object_path (object_path)); + g_return_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name)); + g_return_if_fail (method_name != NULL && g_dbus_is_member_name (method_name)); + g_return_if_fail (timeout_msec >= 0 || timeout_msec == -1); + g_return_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE)); + g_return_if_fail (check_initialized (connection)); +#ifdef G_OS_UNIX + g_return_if_fail (fd_list == NULL || G_IS_UNIX_FD_LIST (fd_list)); +#else + g_return_if_fail (fd_list == NULL); +#endif + + state = g_slice_new0 (CallState); + state->simple = g_simple_async_result_new (G_OBJECT (connection), + callback, user_data, + g_dbus_connection_call_internal); + g_simple_async_result_set_check_cancellable (state->simple, cancellable); + state->method_name = g_strjoin (".", interface_name, method_name, NULL); - if (value == NULL) + if (reply_type == NULL) + reply_type = G_VARIANT_TYPE_ANY; + + state->reply_type = g_variant_type_copy (reply_type); + + message = g_dbus_message_new_method_call (bus_name, + object_path, + interface_name, + method_name); + add_call_flags (message, flags); + if (parameters != NULL) + g_dbus_message_set_body (message, parameters); + +#ifdef G_OS_UNIX + if (fd_list != NULL) + g_dbus_message_set_unix_fd_list (message, fd_list); +#endif + + g_dbus_connection_send_message_with_reply (connection, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + timeout_msec, + &state->serial, + cancellable, + g_dbus_connection_call_done, + state); + + if (G_UNLIKELY (_g_dbus_debug_call ())) { - g_simple_async_result_set_from_error (state->simple, error); - g_error_free (error); + _g_dbus_debug_print_lock (); + g_print ("========================================================================\n" + "GDBus-debug:Call:\n" + " >>>> ASYNC %s.%s()\n" + " on object %s\n" + " owned by name %s (serial %d)\n", + interface_name, + method_name, + object_path, + bus_name != NULL ? bus_name : "(none)", + state->serial); + _g_dbus_debug_print_unlock (); } - else - g_simple_async_result_set_op_res_gpointer (state->simple, value, - (GDestroyNotify) g_variant_unref); - g_simple_async_result_complete (state->simple); - g_variant_type_free (state->reply_type); - g_object_unref (state->simple); - g_free (state->method_name); + if (message != NULL) + g_object_unref (message); +} - g_slice_free (CallState, state); +/* called in any thread, with the connection's lock not held */ +static GVariant * +g_dbus_connection_call_finish_internal (GDBusConnection *connection, + GUnixFDList **out_fd_list, + GAsyncResult *res, + GError **error) +{ + GSimpleAsyncResult *simple; + CallState *state; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (connection), + g_dbus_connection_call_internal), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + simple = G_SIMPLE_ASYNC_RESULT (res); + + if (g_simple_async_result_propagate_error (simple, error)) + return NULL; + + state = g_simple_async_result_get_op_res_gpointer (simple); + if (out_fd_list != NULL) + *out_fd_list = state->fd_list != NULL ? g_object_ref (state->fd_list) : NULL; + return g_variant_ref (state->value); } +/* called in any user thread, with the connection's lock not held */ +static GVariant * +g_dbus_connection_call_sync_internal (GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + gint timeout_msec, + GUnixFDList *fd_list, + GUnixFDList **out_fd_list, + GCancellable *cancellable, + GError **error) +{ + GDBusMessage *message; + GDBusMessage *reply; + GVariant *result; + GError *local_error; + GDBusSendMessageFlags send_flags; + + message = NULL; + reply = NULL; + result = NULL; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (bus_name == NULL || g_dbus_is_name (bus_name), NULL); + g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), NULL); + g_return_val_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name), NULL); + g_return_val_if_fail (method_name != NULL && g_dbus_is_member_name (method_name), NULL); + g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL); + g_return_val_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL); +#ifdef G_OS_UNIX + g_return_val_if_fail (fd_list == NULL || G_IS_UNIX_FD_LIST (fd_list), NULL); +#else + g_return_val_if_fail (fd_list == NULL, NULL); +#endif + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (!(flags & CALL_FLAGS_INITIALIZING)) + g_return_val_if_fail (check_initialized (connection), FALSE); + + if (reply_type == NULL) + reply_type = G_VARIANT_TYPE_ANY; + + message = g_dbus_message_new_method_call (bus_name, + object_path, + interface_name, + method_name); + add_call_flags (message, flags); + if (parameters != NULL) + g_dbus_message_set_body (message, parameters); + +#ifdef G_OS_UNIX + if (fd_list != NULL) + g_dbus_message_set_unix_fd_list (message, fd_list); +#endif + + if (G_UNLIKELY (_g_dbus_debug_call ())) + { + _g_dbus_debug_print_lock (); + g_print ("========================================================================\n" + "GDBus-debug:Call:\n" + " >>>> SYNC %s.%s()\n" + " on object %s\n" + " owned by name %s\n", + interface_name, + method_name, + object_path, + bus_name != NULL ? bus_name : "(none)"); + _g_dbus_debug_print_unlock (); + } + + local_error = NULL; + + send_flags = G_DBUS_SEND_MESSAGE_FLAGS_NONE; + + /* translate from one flavour of flags to another... */ + if (flags & CALL_FLAGS_INITIALIZING) + send_flags |= SEND_MESSAGE_FLAGS_INITIALIZING; + + reply = g_dbus_connection_send_message_with_reply_sync (connection, + message, + send_flags, + timeout_msec, + NULL, /* volatile guint32 *out_serial */ + cancellable, + &local_error); + + if (G_UNLIKELY (_g_dbus_debug_call ())) + { + _g_dbus_debug_print_lock (); + g_print ("========================================================================\n" + "GDBus-debug:Call:\n" + " <<<< SYNC COMPLETE %s.%s()\n" + " ", + interface_name, + method_name); + if (reply != NULL) + { + g_print ("SUCCESS\n"); + } + else + { + g_print ("FAILED: %s\n", + local_error->message); + } + _g_dbus_debug_print_unlock (); + } + + if (reply == NULL) + { + if (error != NULL) + *error = local_error; + else + g_error_free (local_error); + goto out; + } + + result = decode_method_reply (reply, method_name, reply_type, out_fd_list, error); + + out: + if (message != NULL) + g_object_unref (message); + if (reply != NULL) + g_object_unref (reply); + + return result; +} + +/* ---------------------------------------------------------------------------------------------------- */ + /** * g_dbus_connection_call: * @connection: A #GDBusConnection. - * @bus_name: A unique or well-known bus name or %NULL if @connection is not a message bus connection. + * @bus_name: (allow-none): A unique or well-known bus name or %NULL if + * @connection is not a message bus connection. * @object_path: Path of remote object. * @interface_name: D-Bus interface to invoke method on. * @method_name: The name of the method to invoke. - * @parameters: A #GVariant tuple with parameters for the method or %NULL if not passing parameters. - * @reply_type: The expected type of the reply, or %NULL. + * @parameters: (allow-none): A #GVariant tuple with parameters for the method + * or %NULL if not passing parameters. + * @reply_type: (allow-none): The expected type of the reply, or %NULL. * @flags: Flags from the #GDBusCallFlags enumeration. - * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. + * @timeout_msec: The timeout in milliseconds, -1 to use the default + * timeout or %G_MAXINT for no timeout. * @cancellable: A #GCancellable or %NULL. - * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't - * care about the result of the method invocation. + * @callback: (allow-none): A #GAsyncReadyCallback to call when the request is + * satisfied or %NULL if you don't care about the result of the + * method invocation. * @user_data: The data to pass to @callback. * * Asynchronously invokes the @method_name method on the @@ -4871,62 +5628,7 @@ g_dbus_connection_call (GDBusConnection *connection, GAsyncReadyCallback callback, gpointer user_data) { - GDBusMessage *message; - CallState *state; - - g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); - g_return_if_fail (bus_name == NULL || g_dbus_is_name (bus_name)); - g_return_if_fail (object_path != NULL && g_variant_is_object_path (object_path)); - g_return_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name)); - g_return_if_fail (method_name != NULL && g_dbus_is_member_name (method_name)); - g_return_if_fail (timeout_msec >= 0 || timeout_msec == -1); - g_return_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE)); - - state = g_slice_new (CallState); - state->simple = g_simple_async_result_new (G_OBJECT (connection), - callback, user_data, - g_dbus_connection_call); - state->method_name = g_strjoin (".", interface_name, method_name, NULL); - - if (reply_type == NULL) - reply_type = G_VARIANT_TYPE_ANY; - - state->reply_type = g_variant_type_copy (reply_type); - - message = g_dbus_message_new_method_call (bus_name, - object_path, - interface_name, - method_name); - add_call_flags (message, flags); - if (parameters != NULL) - g_dbus_message_set_body (message, parameters); - - g_dbus_connection_send_message_with_reply (connection, - message, - timeout_msec, - &state->serial, - cancellable, - g_dbus_connection_call_done, - state); - - if (G_UNLIKELY (_g_dbus_debug_call ())) - { - _g_dbus_debug_print_lock (); - g_print ("========================================================================\n" - "GDBus-debug:Call:\n" - " >>>> ASYNC %s.%s()\n" - " on object %s\n" - " owned by name %s (serial %d)\n", - interface_name, - method_name, - object_path, - bus_name != NULL ? bus_name : "(none)", - state->serial); - _g_dbus_debug_print_unlock (); - } - - if (message != NULL) - g_object_unref (message); + g_dbus_connection_call_internal (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, NULL, cancellable, callback, user_data); } /** @@ -4947,23 +5649,9 @@ g_dbus_connection_call_finish (GDBusConnection *connection, GAsyncResult *res, GError **error) { - GSimpleAsyncResult *simple; - - g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); - g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (connection), - g_dbus_connection_call), NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - simple = G_SIMPLE_ASYNC_RESULT (res); - - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - - return g_variant_ref (g_simple_async_result_get_op_res_gpointer (simple)); + return g_dbus_connection_call_finish_internal (connection, NULL, res, error); } -/* ---------------------------------------------------------------------------------------------------- */ - /** * g_dbus_connection_call_sync: * @connection: A #GDBusConnection. @@ -4971,10 +5659,12 @@ g_dbus_connection_call_finish (GDBusConnection *connection, * @object_path: Path of remote object. * @interface_name: D-Bus interface to invoke method on. * @method_name: The name of the method to invoke. - * @parameters: A #GVariant tuple with parameters for the method or %NULL if not passing parameters. - * @reply_type: The expected type of the reply, or %NULL. + * @parameters: (allow-none): A #GVariant tuple with parameters for the method + * or %NULL if not passing parameters. + * @reply_type: (allow-none): The expected type of the reply, or %NULL. * @flags: Flags from the #GDBusCallFlags enumeration. - * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout. + * @timeout_msec: The timeout in milliseconds, -1 to use the default + * timeout or %G_MAXINT for no timeout. * @cancellable: A #GCancellable or %NULL. * @error: Return location for error or %NULL. * @@ -5033,98 +5723,128 @@ g_dbus_connection_call_sync (GDBusConnection *connection, GCancellable *cancellable, GError **error) { - GDBusMessage *message; - GDBusMessage *reply; - GVariant *result; - GError *local_error; - - message = NULL; - reply = NULL; - result = NULL; - - g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); - g_return_val_if_fail (bus_name == NULL || g_dbus_is_name (bus_name), NULL); - g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), NULL); - g_return_val_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name), NULL); - g_return_val_if_fail (method_name != NULL && g_dbus_is_member_name (method_name), NULL); - g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL); - g_return_val_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL); - - if (reply_type == NULL) - reply_type = G_VARIANT_TYPE_ANY; - - message = g_dbus_message_new_method_call (bus_name, - object_path, - interface_name, - method_name); - add_call_flags (message, flags); - if (parameters != NULL) - g_dbus_message_set_body (message, parameters); - - if (G_UNLIKELY (_g_dbus_debug_call ())) - { - _g_dbus_debug_print_lock (); - g_print ("========================================================================\n" - "GDBus-debug:Call:\n" - " >>>> SYNC %s.%s()\n" - " on object %s\n" - " owned by name %s\n", - interface_name, - method_name, - object_path, - bus_name != NULL ? bus_name : "(none)"); - _g_dbus_debug_print_unlock (); - } - - local_error = NULL; - reply = g_dbus_connection_send_message_with_reply_sync (connection, - message, - timeout_msec, - NULL, /* volatile guint32 *out_serial */ - cancellable, - &local_error); + return g_dbus_connection_call_sync_internal (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, NULL, NULL, cancellable, error); +} - if (G_UNLIKELY (_g_dbus_debug_call ())) - { - _g_dbus_debug_print_lock (); - g_print ("========================================================================\n" - "GDBus-debug:Call:\n" - " <<<< SYNC COMPLETE %s.%s()\n" - " ", - interface_name, - method_name); - if (reply != NULL) - { - g_print ("SUCCESS\n"); - } - else - { - g_print ("FAILED: %s\n", - local_error->message); - } - _g_dbus_debug_print_unlock (); - } +/* ---------------------------------------------------------------------------------------------------- */ - if (reply == NULL) - { - if (error != NULL) - *error = local_error; - else - g_error_free (local_error); - goto out; - } +#ifdef G_OS_UNIX - result = decode_method_reply (reply, method_name, reply_type, error); +/** + * g_dbus_connection_call_with_unix_fd_list: + * @connection: A #GDBusConnection. + * @bus_name: (allow-none): A unique or well-known bus name or %NULL if + * @connection is not a message bus connection. + * @object_path: Path of remote object. + * @interface_name: D-Bus interface to invoke method on. + * @method_name: The name of the method to invoke. + * @parameters: (allow-none): A #GVariant tuple with parameters for the method + * or %NULL if not passing parameters. + * @reply_type: (allow-none): The expected type of the reply, or %NULL. + * @flags: Flags from the #GDBusCallFlags enumeration. + * @timeout_msec: The timeout in milliseconds, -1 to use the default + * timeout or %G_MAXINT for no timeout. + * @fd_list: (allow-none): A #GUnixFDList or %NULL. + * @cancellable: A #GCancellable or %NULL. + * @callback: (allow-none): A #GAsyncReadyCallback to call when the request is + * satisfied or %NULL if you don't * care about the result of the + * method invocation. + * @user_data: The data to pass to @callback. + * + * Like g_dbus_connection_call() but also takes a #GUnixFDList object. + * + * This method is only available on UNIX. + * + * Since: 2.30 + */ +void +g_dbus_connection_call_with_unix_fd_list (GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + gint timeout_msec, + GUnixFDList *fd_list, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_dbus_connection_call_internal (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, fd_list, cancellable, callback, user_data); +} - out: - if (message != NULL) - g_object_unref (message); - if (reply != NULL) - g_object_unref (reply); +/** + * g_dbus_connection_call_with_unix_fd_list_finish: + * @connection: A #GDBusConnection. + * @out_fd_list: (out): Return location for a #GUnixFDList or %NULL. + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_call_with_unix_fd_list(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with g_dbus_connection_call_with_unix_fd_list(). + * + * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with + * return values. Free with g_variant_unref(). + * + * Since: 2.30 + */ +GVariant * +g_dbus_connection_call_with_unix_fd_list_finish (GDBusConnection *connection, + GUnixFDList **out_fd_list, + GAsyncResult *res, + GError **error) +{ + return g_dbus_connection_call_finish_internal (connection, out_fd_list, res, error); +} - return result; +/** + * g_dbus_connection_call_with_unix_fd_list_sync: + * @connection: A #GDBusConnection. + * @bus_name: A unique or well-known bus name. + * @object_path: Path of remote object. + * @interface_name: D-Bus interface to invoke method on. + * @method_name: The name of the method to invoke. + * @parameters: (allow-none): A #GVariant tuple with parameters for the method + * or %NULL if not passing parameters. + * @reply_type: (allow-none): The expected type of the reply, or %NULL. + * @flags: Flags from the #GDBusCallFlags enumeration. + * @timeout_msec: The timeout in milliseconds, -1 to use the default + * timeout or %G_MAXINT for no timeout. + * @fd_list: (allow-none): A #GUnixFDList or %NULL. + * @out_fd_list: (out): Return location for a #GUnixFDList or %NULL. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Like g_dbus_connection_call_sync() but also takes and returns #GUnixFDList objects. + * + * This method is only available on UNIX. + * + * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with + * return values. Free with g_variant_unref(). + * + * Since: 2.30 + */ +GVariant * +g_dbus_connection_call_with_unix_fd_list_sync (GDBusConnection *connection, + const gchar *bus_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + const GVariantType *reply_type, + GDBusCallFlags flags, + gint timeout_msec, + GUnixFDList *fd_list, + GUnixFDList **out_fd_list, + GCancellable *cancellable, + GError **error) +{ + return g_dbus_connection_call_sync_internal (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, fd_list, out_fd_list, cancellable, error); } +#endif /* G_OS_UNIX */ + /* ---------------------------------------------------------------------------------------------------- */ struct ExportedSubtree @@ -5143,19 +5863,20 @@ struct ExportedSubtree static void exported_subtree_free (ExportedSubtree *es) { - if (es->user_data_free_func != NULL) - /* TODO: push to thread-default mainloop */ - es->user_data_free_func (es->user_data); + call_destroy_notify (es->context, + es->user_data_free_func, + es->user_data); - if (es->context != NULL) - g_main_context_unref (es->context); + g_main_context_unref (es->context); _g_dbus_subtree_vtable_free (es->vtable); g_free (es->object_path); g_free (es); } -/* called without lock held */ +/* called without lock held in the thread where the caller registered + * the subtree + */ static gboolean handle_subtree_introspect (GDBusConnection *connection, ExportedSubtree *es, @@ -5172,6 +5893,8 @@ handle_subtree_introspect (GDBusConnection *connection, GDBusInterfaceInfo **interfaces; guint n; gchar **subnode_paths; + gboolean has_properties_interface; + gboolean has_introspectable_interface; handled = FALSE; @@ -5211,7 +5934,20 @@ handle_subtree_introspect (GDBusConnection *connection, es->user_data); if (interfaces != NULL) { - introspect_append_standard_interfaces (s); + has_properties_interface = FALSE; + has_introspectable_interface = FALSE; + + for (n = 0; interfaces[n] != NULL; n++) + { + if (strcmp (interfaces[n]->name, "org.freedesktop.DBus.Properties") == 0) + has_properties_interface = TRUE; + else if (strcmp (interfaces[n]->name, "org.freedesktop.DBus.Introspectable") == 0) + has_introspectable_interface = TRUE; + } + if (!has_properties_interface) + g_string_append (s, introspect_properties_interface); + if (!has_introspectable_interface) + g_string_append (s, introspect_introspectable_interface); for (n = 0; interfaces[n] != NULL; n++) { @@ -5238,7 +5974,7 @@ handle_subtree_introspect (GDBusConnection *connection, reply = g_dbus_message_new_method_reply (message); g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); - g_dbus_connection_send_message (connection, reply, NULL, NULL); + g_dbus_connection_send_message (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; @@ -5249,7 +5985,9 @@ handle_subtree_introspect (GDBusConnection *connection, return handled; } -/* called without lock held */ +/* called without lock held in the thread where the caller registered + * the subtree + */ static gboolean handle_subtree_method_invocation (GDBusConnection *connection, ExportedSubtree *es, @@ -5396,7 +6134,7 @@ handle_subtree_method_invocation (GDBusConnection *connection, "org.freedesktop.DBus.Error.InvalidArgs", _("No such interface `%s'"), interface_name); - g_dbus_connection_send_message (es->connection, reply, NULL, NULL); + g_dbus_connection_send_message (es->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); handled = TRUE; goto out; @@ -5509,14 +6247,14 @@ process_subtree_vtable_message_in_idle_cb (gpointer _data) g_dbus_message_get_member (data->message), g_dbus_message_get_interface (data->message), g_dbus_message_get_signature (data->message)); - g_dbus_connection_send_message (data->es->connection, reply, NULL, NULL); + g_dbus_connection_send_message (data->es->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); } return FALSE; } -/* called in message handler thread with lock held */ +/* called in GDBusWorker thread with connection's lock held */ static gboolean subtree_message_func (GDBusConnection *connection, ExportedSubtree *es, @@ -5612,6 +6350,7 @@ g_dbus_connection_register_subtree (GDBusConnection *connection, g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), 0); g_return_val_if_fail (vtable != NULL, 0); g_return_val_if_fail (error == NULL || *error == NULL, 0); + g_return_val_if_fail (check_initialized (connection), 0); ret = 0; @@ -5637,9 +6376,7 @@ g_dbus_connection_register_subtree (GDBusConnection *connection, es->id = _global_subtree_registration_id++; /* TODO: overflow etc. */ es->user_data = user_data; es->user_data_free_func = user_data_free_func; - es->context = g_main_context_get_thread_default (); - if (es->context != NULL) - g_main_context_ref (es->context); + es->context = g_main_context_ref_thread_default (); g_hash_table_insert (connection->map_object_path_to_es, es->object_path, es); g_hash_table_insert (connection->map_id_to_es, @@ -5675,6 +6412,7 @@ g_dbus_connection_unregister_subtree (GDBusConnection *connection, gboolean ret; g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE); + g_return_val_if_fail (check_initialized (connection), FALSE); ret = FALSE; @@ -5698,7 +6436,7 @@ g_dbus_connection_unregister_subtree (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ -/* must be called with lock held */ +/* may be called in any thread, with connection's lock held */ static void handle_generic_ping_unlocked (GDBusConnection *connection, const gchar *object_path, @@ -5706,11 +6444,11 @@ handle_generic_ping_unlocked (GDBusConnection *connection, { GDBusMessage *reply; reply = g_dbus_message_new_method_reply (message); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); } -/* must be called with lock held */ +/* may be called in any thread, with connection's lock held */ static void handle_generic_get_machine_id_unlocked (GDBusConnection *connection, const gchar *object_path, @@ -5739,11 +6477,11 @@ handle_generic_get_machine_id_unlocked (GDBusConnection *connection, reply = g_dbus_message_new_method_reply (message); g_dbus_message_set_body (reply, g_variant_new ("(s)", connection->machine_id)); } - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); } -/* must be called with lock held */ +/* may be called in any thread, with connection's lock held */ static void handle_generic_introspect_unlocked (GDBusConnection *connection, const gchar *object_path, @@ -5766,12 +6504,12 @@ handle_generic_introspect_unlocked (GDBusConnection *connection, reply = g_dbus_message_new_method_reply (message); g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str)); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); g_string_free (s, TRUE); } -/* must be called with lock held */ +/* may be called in any thread, with connection's lock held */ static gboolean handle_generic_unlocked (GDBusConnection *connection, GDBusMessage *message) @@ -5818,7 +6556,7 @@ handle_generic_unlocked (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ -/* called in message handler thread with lock held */ +/* called in GDBusWorker thread with connection's lock held */ static void distribute_method_call (GDBusConnection *connection, GDBusMessage *message) @@ -5829,7 +6567,6 @@ distribute_method_call (GDBusConnection *connection, const gchar *object_path; const gchar *interface_name; const gchar *member; - const gchar *signature; const gchar *path; gchar *subtree_path; gchar *needle; @@ -5838,7 +6575,6 @@ distribute_method_call (GDBusConnection *connection, interface_name = g_dbus_message_get_interface (message); member = g_dbus_message_get_member (message); - signature = g_dbus_message_get_signature (message); path = g_dbus_message_get_path (message); subtree_path = g_strdup (path); needle = strrchr (subtree_path, '/'); @@ -5858,23 +6594,17 @@ distribute_method_call (GDBusConnection *connection, _g_dbus_debug_print_lock (); g_print ("========================================================================\n" "GDBus-debug:Incoming:\n" - " >>>> METHOD INVOCATION %s.%s()\n" + " <<<< METHOD INVOCATION %s.%s()\n" " on object %s\n" - " invoked by name %s\n", + " invoked by name %s\n" + " serial %d\n", interface_name, member, path, - g_dbus_message_get_sender (message) != NULL ? g_dbus_message_get_sender (message) : "(none)"); + g_dbus_message_get_sender (message) != NULL ? g_dbus_message_get_sender (message) : "(none)", + g_dbus_message_get_serial (message)); _g_dbus_debug_print_unlock (); } -#if 0 - g_debug ("interface = `%s'", interface_name); - g_debug ("member = `%s'", member); - g_debug ("signature = `%s'", signature); - g_debug ("path = `%s'", path); - g_debug ("subtree_path = `%s'", subtree_path != NULL ? subtree_path : "N/A"); -#endif - object_path = g_dbus_message_get_path (message); g_assert (object_path != NULL); @@ -5911,7 +6641,7 @@ distribute_method_call (GDBusConnection *connection, _("No such interface `%s' on object at path %s"), interface_name, object_path); - g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); out: @@ -5920,11 +6650,12 @@ distribute_method_call (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ -static GDBusConnection ** +/* Called in any user thread, with the message_bus_lock held. */ +static GWeakRef * message_bus_get_singleton (GBusType bus_type, GError **error) { - GDBusConnection **ret; + GWeakRef *ret; const gchar *starter_bus; ret = NULL; @@ -5982,12 +6713,13 @@ message_bus_get_singleton (GBusType bus_type, return ret; } +/* Called in any user thread, without holding locks. */ static GDBusConnection * get_uninitialized_connection (GBusType bus_type, GCancellable *cancellable, GError **error) { - GDBusConnection **singleton; + GWeakRef *singleton; GDBusConnection *ret; ret = NULL; @@ -5997,24 +6729,24 @@ get_uninitialized_connection (GBusType bus_type, if (singleton == NULL) goto out; - if (*singleton == NULL) + ret = g_weak_ref_get (singleton); + + if (ret == NULL) { gchar *address; address = g_dbus_address_get_for_bus_sync (bus_type, cancellable, error); if (address == NULL) goto out; - ret = *singleton = g_object_new (G_TYPE_DBUS_CONNECTION, - "address", address, - "flags", G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | - G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, - "exit-on-close", TRUE, - NULL); + ret = g_object_new (G_TYPE_DBUS_CONNECTION, + "address", address, + "flags", G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | + G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, + "exit-on-close", TRUE, + NULL); + + g_weak_ref_set (singleton, ret); g_free (address); } - else - { - ret = g_object_ref (*singleton); - } g_assert (ret != NULL); @@ -6046,7 +6778,7 @@ get_uninitialized_connection (GBusType bus_type, * Note that the returned #GDBusConnection object will (usually) have * the #GDBusConnection:exit-on-close property set to %TRUE. * - * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + * Returns: (transfer full): A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). * * Since: 2.26 */ @@ -6087,8 +6819,7 @@ bus_get_async_initable_cb (GObject *source_object, &error)) { g_assert (error != NULL); - g_simple_async_result_set_from_error (simple, error); - g_error_free (error); + g_simple_async_result_take_error (simple, error); g_object_unref (source_object); } else @@ -6132,14 +6863,14 @@ g_bus_get (GBusType bus_type, callback, user_data, g_bus_get); + g_simple_async_result_set_check_cancellable (simple, cancellable); error = NULL; connection = get_uninitialized_connection (bus_type, cancellable, &error); if (connection == NULL) { g_assert (error != NULL); - g_simple_async_result_set_from_error (simple, error); - g_error_free (error); + g_simple_async_result_take_error (simple, error); g_simple_async_result_complete_in_idle (simple); g_object_unref (simple); } @@ -6163,13 +6894,13 @@ g_bus_get (GBusType bus_type, * The returned object is a singleton, that is, shared with other * callers of g_bus_get() and g_bus_get_sync() for @bus_type. In the * event that you need a private message bus connection, use - * g_dbus_address_get_for_bus() and + * g_dbus_address_get_for_bus_sync() and * g_dbus_connection_new_for_address(). * * Note that the returned #GDBusConnection object will (usually) have * the #GDBusConnection:exit-on-close property set to %TRUE. * - * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + * Returns: (transfer full): A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). * * Since: 2.26 */