X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgdbusconnection.c;h=680062e951f1863be461425a7e0e1c82d455eaec;hb=3389489f0f25a3a324ad62d9df5a6f1c038d5986;hp=643c0e423396f79a1352a256978d6557c1c5aebb;hpb=1b28408b8bfe282f671716a39bba3208d3fef39a;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 643c0e4..680062e 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -13,9 +13,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. + * Public License along with this library; if not, see . * * Author: David Zeuthen */ @@ -105,10 +103,6 @@ #include #include -#include -#ifdef HAVE_UNISTD_H -#include -#endif #include "gdbusauth.h" #include "gdbusutils.h" @@ -128,6 +122,8 @@ #include "gsimpleasyncresult.h" #ifdef G_OS_UNIX +#include "gkdbus.h" +#include "gkdbusconnection.h" #include "gunixconnection.h" #include "gunixfdmessage.h" #endif @@ -145,16 +141,49 @@ * over any transport that can by represented as an #GIOStream. * * This class is rarely used directly in D-Bus clients. If you are writing - * an D-Bus client, it is often easier to use the g_bus_own_name(), + * a 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. * - * D-Bus server exampleFIXME: MISSING XINCLUDE CONTENT + * 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(). + * + * ## An example D-Bus server # {#gdbus-server} + * + * Here is an example for a D-Bus server: + * [gdbus-example-server.c](https://git.gnome.org/browse/glib/tree/gio/tests/gdbus-example-server.c) + * + * ## An example for exporting a subtree # {#gdbus-subtree-server} + * + * Here is an example for exporting a subtree: + * [gdbus-example-subtree.c](https://git.gnome.org/browse/glib/tree/gio/tests/gdbus-example-subtree.c) + * + * ## An example for file descriptor passing # {#gdbus-unix-fd-client} * - * D-Bus subtree exampleFIXME: MISSING XINCLUDE CONTENT + * Here is an example for passing UNIX file descriptors: + * [gdbus-unix-fd-client.c](https://git.gnome.org/browse/glib/tree/gio/tests/gdbus-unix-fd-client.c) * - * D-Bus UNIX File Descriptor exampleFIXME: MISSING XINCLUDE CONTENT + * ## An example for exporting a GObject # {#gdbus-export} * - * Exporting a GObjectFIXME: MISSING XINCLUDE CONTENT + * Here is an example for exporting a #GObject: + * [gdbus-example-export.c](https://git.gnome.org/browse/glib/tree/gio/tests/gdbus-example-export.c) */ /* ---------------------------------------------------------------------------------------------------- */ @@ -183,8 +212,21 @@ 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; +static GWeakRef the_user_bus; +static GWeakRef the_machine_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) /* ---------------------------------------------------------------------------------------------------- */ @@ -213,8 +255,8 @@ call_destroy_notify_data_free (CallDestroyNotifyData *data) /* * call_destroy_notify: - * @context: A #GMainContext or %NULL. - * @callback: A #GDestroyNotify or %NULL. + * @context: (allow-none): A #GMainContext or %NULL. + * @callback: (allow-none): A #GDestroyNotify or %NULL. * @user_data: Data to pass to @callback. * * Schedules @callback to run in @context. @@ -224,34 +266,28 @@ call_destroy_notify (GMainContext *context, GDestroyNotify callback, gpointer user_data) { + GSource *idle_source; + CallDestroyNotifyData *data; + if (callback == NULL) goto out; - if (context == g_main_context_get_thread_default ()) - { - callback (user_data); - } - else - { - GSource *idle_source; - CallDestroyNotifyData *data; - - 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); + 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); - } + 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_set_name (idle_source, "[gio] call_destroy_notify_data_in_idle"); + g_source_attach (idle_source, data->context); + g_source_unref (idle_source); out: ; @@ -280,7 +316,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"); \ @@ -289,13 +325,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: * @@ -313,84 +356,119 @@ 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.Peer.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 */ - GPtrArray *filters; + /* Map used for storing last used serials for each thread, protected by @lock */ + GHashTable *map_thread_to_last_serial; - /* Whether to exit on close */ - gboolean exit_on_close; + /* Structure used for message filters, protected by @lock */ + GPtrArray *filters; - /* 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; + + /* 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 */ @@ -436,11 +514,23 @@ static gboolean handle_generic_unlocked (GDBusConnection *connection, static void purge_all_signal_subscriptions (GDBusConnection *connection); static void purge_all_filters (GDBusConnection *connection); +static void schedule_method_call (GDBusConnection *connection, + GDBusMessage *message, + guint registration_id, + guint subtree_registration_id, + const GDBusInterfaceInfo *interface_info, + const GDBusMethodInfo *method_info, + const GDBusPropertyInfo *property_info, + GVariant *parameters, + const GDBusInterfaceVTable *vtable, + GMainContext *main_context, + gpointer user_data); + #define _G_ENSURE_LOCK(name) do { \ if (G_UNLIKELY (G_TRYLOCK(name))) \ { \ g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ - "_G_ENSURE_LOCK: Lock `" #name "' is not locked"); \ + "_G_ENSURE_LOCK: Lock '" #name "' is not locked"); \ } \ } while (FALSE) \ @@ -454,6 +544,82 @@ G_DEFINE_TYPE_WITH_CODE (GDBusConnection, g_dbus_connection, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init) ); +/* + * 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) +{ + /* 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_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)) + { + g_return_val_if_fail (flags & FLAG_INITIALIZED, FALSE); + g_return_val_if_fail (connection->initialization_error == NULL, FALSE); + } + + if (flags & FLAG_CLOSED) + { + 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 @@ -462,14 +628,6 @@ g_dbus_connection_dispose (GObject *object) GDBusConnection *connection = G_DBUS_CONNECTION (object); G_LOCK (message_bus_lock); - if (connection == the_session_bus) - { - the_session_bus = NULL; - } - else if (connection == the_system_bus) - { - the_system_bus = NULL; - } CONNECTION_LOCK (connection); if (connection->worker != NULL) { @@ -513,12 +671,6 @@ g_dbus_connection_finalize (GObject *object) 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; } @@ -542,17 +694,19 @@ 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); - if (connection->main_context_at_construction != NULL) - g_main_context_unref (connection->main_context_at_construction); + g_hash_table_unref (connection->map_thread_to_last_serial); + + 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, @@ -593,6 +747,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, @@ -633,23 +788,36 @@ 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 && connection->is_initialized) + 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) { 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); + g_printerr ("%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); + g_printerr ("%s: Remote peer vanished. Exiting.\n", G_STRFUNC); } raise (SIGTERM); } @@ -677,6 +845,10 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * 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, @@ -805,8 +977,11 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) * GDBusConnection:exit-on-close: * * A boolean specifying whether the process will be terminated (by - * calling raise(SIGTERM)) if the connection - * is closed by the remote peer. + * 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 */ @@ -863,29 +1038,23 @@ g_dbus_connection_class_init (GDBusConnectionClass *klass) /** * GDBusConnection::closed: - * @connection: The #GDBusConnection emitting the signal. + * @connection: the #GDBusConnection emitting the signal * @remote_peer_vanished: %TRUE if @connection is closed because the - * remote peer closed its end of the connection. - * @error: A #GError with more details about the event or %NULL. + * remote peer closed its end of the connection + * @error: (allow-none): a #GError with more details about the event or %NULL * * Emitted when the connection is closed. * * The cause of this event can be - * - * - * If g_dbus_connection_close() is called. In this case - * @remote_peer_vanished is set to %FALSE and @error is %NULL. - * - * - * If the remote peer closes the connection. In this case - * @remote_peer_vanished is set to %TRUE and @error is set. - * - * - * If the remote peer sends invalid or malformed data. In this - * case @remote_peer_vanished is set to %FALSE and @error - * is set. - * - * + * + * - If g_dbus_connection_close() is called. In this case + * @remote_peer_vanished is set to %FALSE and @error is %NULL. + * + * - If the remote peer closes the connection. In this case + * @remote_peer_vanished is set to %TRUE and @error is set. + * + * - If the remote peer sends invalid or malformed data. In this + * case @remote_peer_vanished is set to %FALSE and @error is set. * * Upon receiving this signal, you should give up your reference to * @connection. You are guaranteed that this signal is emitted only @@ -909,8 +1078,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); @@ -939,9 +1108,10 @@ 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->map_thread_to_last_serial = g_hash_table_new (g_direct_hash, + g_direct_equal); + + connection->main_context_at_construction = g_main_context_ref_thread_default (); connection->filters = g_ptr_array_new (); } @@ -952,6 +1122,10 @@ g_dbus_connection_init (GDBusConnection *connection) * * Gets the underlying 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 @@ -960,12 +1134,17 @@ 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; } /** * g_dbus_connection_start_message_processing: - * @connection: A #GDBusConnection. + * @connection: a #GDBusConnection * * If @connection was created with * %G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING, this method @@ -978,33 +1157,44 @@ 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); } /** * g_dbus_connection_is_closed: - * @connection: A #GDBusConnection. + * @connection: a #GDBusConnection * * Gets whether @connection is closed. * - * Returns: %TRUE if the connection is closed, %FALSE otherwise. + * Returns: %TRUE if the connection is closed, %FALSE otherwise * * Since: 2.26 */ 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; } /** * g_dbus_connection_get_capabilities: - * @connection: A #GDBusConnection. + * @connection: a #GDBusConnection * * Gets the capabilities negotiated with the remote peer * - * Returns: Zero or more flags from the #GDBusCapabilityFlags enumeration. + * Returns: zero or more flags from the #GDBusCapabilityFlags enumeration * * Since: 2.26 */ @@ -1012,11 +1202,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, @@ -1033,26 +1229,25 @@ flush_in_thread_func (GSimpleAsyncResult *res, /** * g_dbus_connection_flush: - * @connection: A #GDBusConnection. - * @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. - * @user_data: The data to pass to @callback. + * @connection: a #GDBusConnection + * @cancellable: (allow-none): 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 + * @user_data: The data to pass to @callback * * Asynchronously flushes @connection, that is, writes all queued * outgoing message to the transport and then flushes the transport * (using g_output_stream_flush_async()). This is useful in programs - * that wants to emit a D-Bus signal and then exit - * immediately. Without flushing the connection, there is no guarantee - * that the message has been sent to the networking buffers in the OS - * kernel. + * that wants to emit a D-Bus signal and then exit immediately. Without + * flushing the connection, there is no guaranteed that the message has + * been sent to the networking buffers in the OS kernel. * * This is an asynchronous method. When the operation is finished, - * @callback will be invoked in the thread-default main - * loop of the thread you are calling this method from. You can + * @callback will be invoked in the + * [thread-default main context][g-main-context-push-thread-default] + * of the thread you are calling this method from. You can * then call g_dbus_connection_flush_finish() to get the result of the - * operation. See g_dbus_connection_flush_sync() for the synchronous + * operation. See g_dbus_connection_flush_sync() for the synchronous * version. * * Since: 2.26 @@ -1071,6 +1266,7 @@ g_dbus_connection_flush (GDBusConnection *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, @@ -1080,13 +1276,14 @@ g_dbus_connection_flush (GDBusConnection *connection, /** * g_dbus_connection_flush_finish: - * @connection: A #GDBusConnection. - * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_flush(). - * @error: Return location for error or %NULL. + * @connection: a #GDBusConnection + * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed + * to g_dbus_connection_flush() + * @error: return location for error or %NULL * * Finishes an operation started with g_dbus_connection_flush(). * - * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. + * Returns: %TRUE if the operation succeeded, %FALSE if @error is set * * Since: 2.26 */ @@ -1117,16 +1314,16 @@ g_dbus_connection_flush_finish (GDBusConnection *connection, /** * g_dbus_connection_flush_sync: - * @connection: A #GDBusConnection. - * @cancellable: A #GCancellable or %NULL. - * @error: Return location for error or %NULL. + * @connection: a #GDBusConnection + * @cancellable: (allow-none): a #GCancellable or %NULL + * @error: return location for error or %NULL * * Synchronously flushes @connection. The calling thread is blocked * until this is done. See g_dbus_connection_flush() for the * asynchronous version of this method and more details about what it * does. * - * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. + * Returns: %TRUE if the operation succeeded, %FALSE if @error is set * * Since: 2.26 */ @@ -1142,14 +1339,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, @@ -1177,6 +1377,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) { @@ -1193,21 +1396,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; @@ -1219,33 +1420,20 @@ set_closed_unlocked (GDBusConnection *connection, emit_closed_in_idle, data, (GDestroyNotify) emit_closed_data_free); + g_source_set_name (idle_source, "[gio] emit_closed_in_idle"); g_source_attach (idle_source, connection->main_context_at_construction); g_source_unref (idle_source); } /* ---------------------------------------------------------------------------------------------------- */ -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_take_error (res, error); -} - /** * g_dbus_connection_close: - * @connection: A #GDBusConnection. - * @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. - * @user_data: The data to pass to @callback. + * @connection: a #GDBusConnection + * @cancellable: (allow-none): 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 + * @user_data: The data to pass to @callback * * Closes @connection. Note that this never causes the process to * exit (this might only happen if the other end of a shared message @@ -1260,16 +1448,16 @@ close_in_thread_func (GSimpleAsyncResult *res, * %G_IO_ERROR_CLOSED. * * When @connection has been closed, the #GDBusConnection::closed - * signal is emitted in the thread-default main - * loop of the thread that @connection was constructed in. + * signal is emitted in the + * [thread-default main context][g-main-context-push-thread-default] + * of the thread that @connection was constructed in. * * This is an asynchronous method. When the operation is finished, - * @callback will be invoked in the thread-default main - * loop of the thread you are calling this method from. You can + * @callback will be invoked in the + * [thread-default main context][g-main-context-push-thread-default] + * of the thread you are calling this method from. You can * then call g_dbus_connection_close_finish() to get the result of the - * operation. See g_dbus_connection_close_sync() for the synchronous + * operation. See g_dbus_connection_close_sync() for the synchronous * version. * * Since: 2.26 @@ -1284,26 +1472,31 @@ g_dbus_connection_close (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); + 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); } /** * g_dbus_connection_close_finish: - * @connection: A #GDBusConnection. - * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_close(). - * @error: Return location for error or %NULL. + * @connection: a #GDBusConnection + * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed + * to g_dbus_connection_close() + * @error: return location for error or %NULL * * Finishes an operation started with g_dbus_connection_close(). * - * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. + * Returns: %TRUE if the operation succeeded, %FALSE if @error is set * * Since: 2.26 */ @@ -1332,25 +1525,42 @@ 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. - * @cancellable: A #GCancellable or %NULL. - * @error: Return location for error or %NULL. + * @connection: a #GDBusConnection + * @cancellable: (allow-none): a #GCancellable or %NULL + * @error: return location for error or %NULL * * Synchronously closees @connection. The calling thread is blocked * until this is done. See g_dbus_connection_close() for the * asynchronous version of this method and more details about what it * does. * - * Returns: %TRUE if the operation succeeded, %FALSE if @error is set. + * Returns: %TRUE if the operation succeeded, %FALSE if @error is set * * Since: 2.26 */ gboolean -g_dbus_connection_close_sync (GDBusConnection *connection, - GCancellable *cancellable, - GError **error) +g_dbus_connection_close_sync (GDBusConnection *connection, + GCancellable *cancellable, + GError **error) { gboolean ret; @@ -1359,22 +1569,569 @@ g_dbus_connection_close_sync (GDBusConnection *connection, ret = FALSE; - CONNECTION_LOCK (connection); - if (!connection->closed) + if (check_unclosed (connection, 0, error)) { - ret = g_io_stream_close (connection->stream, - cancellable, - error); - if (ret) - set_closed_unlocked (connection, FALSE, NULL); + 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); } + + return ret; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_request_name: + * @connection: a #GDBusConnection + * @name: well-known bus name (name to request) + * @flags: a set of flags from the GBusNameOwnerFlags enumeration + * @error: return location for error or %NULL + * + * Synchronously acquires name on the bus and returns status code. + * + * The calling thread is blocked until a reply is received. + * + * Returns: status code or %G_BUS_REQUEST_NAME_FLAGS_ERROR + * if @error is set. + * + * Since: 2.4x + */ +GBusRequestNameReplyFlags +g_dbus_request_name (GDBusConnection *connection, + const gchar *name, + GBusNameOwnerFlags flags, + GError **error) +{ + GVariant *result; + guint32 request_name_reply; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), G_BUS_RELEASE_NAME_FLAGS_ERROR); + g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), G_BUS_RELEASE_NAME_FLAGS_ERROR); + g_return_val_if_fail (error == NULL || *error == NULL, G_BUS_RELEASE_NAME_FLAGS_ERROR); + + if (G_IS_KDBUS_CONNECTION (connection->stream)) + result = _g_kdbus_RequestName (connection, name, flags, error); else + result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/org/freedesktop/DBus", + "org.freedesktop.DBus", "RequestName", + g_variant_new ("(su)", name, flags), G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); + + if (result != NULL) { - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_CLOSED, - _("The connection is closed")); + g_variant_get (result, "(u)", &request_name_reply); + g_variant_unref (result); } + else + request_name_reply = G_BUS_REQUEST_NAME_FLAGS_ERROR; + + return (GBusRequestNameReplyFlags) request_name_reply; +} + +/** + * g_dbus_release_name: + * @connection: a #GDBusConnection + * @name: well-known bus name (name to release) + * @error: return location for error or %NULL + * + * Synchronously releases name on the bus and returns status code. + * + * The calling thread is blocked until a reply is received. + * + * Returns: status code or %G_BUS_RELEASE_NAME_FLAGS_ERROR + * if @error is set. + * + * Since: 2.4x + */ +GBusReleaseNameReplyFlags +g_dbus_release_name (GDBusConnection *connection, + const gchar *name, + GError **error) +{ + GVariant *result; + guint32 release_name_reply; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), G_BUS_RELEASE_NAME_FLAGS_ERROR); + g_return_val_if_fail (g_dbus_is_name (name) && !g_dbus_is_unique_name (name), G_BUS_RELEASE_NAME_FLAGS_ERROR); + g_return_val_if_fail (error == NULL || *error == NULL, G_BUS_RELEASE_NAME_FLAGS_ERROR); + + if (G_IS_KDBUS_CONNECTION (connection->stream)) + result = _g_kdbus_ReleaseName (connection, name, error); + else + result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/org/freedesktop/DBus", + "org.freedesktop.DBus", "ReleaseName", + g_variant_new ("(s)", name), G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); + + if (result != NULL) + { + g_variant_get (result, "(u)", &release_name_reply); + g_variant_unref (result); + } + else + release_name_reply = G_BUS_RELEASE_NAME_FLAGS_ERROR; + + return (GBusReleaseNameReplyFlags) release_name_reply; +} + +/** + * g_dbus_get_bus_id: + * @connection: a #GDBusConnection + * @error: return location for error or %NULL + * + * Synchronously returns the unique ID of the bus. + * + * The calling thread is blocked until a reply is received. + * + * Returns: the unique ID of the bus or %NULL if @error is set. + * Free with g_free(). + * + * Since: 2.4x + */ +gchar * +g_dbus_get_bus_id (GDBusConnection *connection, + GError **error) +{ + GVariant *result; + gchar *bus_id; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + result = NULL; + bus_id = NULL; + + if (G_IS_KDBUS_CONNECTION (connection->stream)) + { + result = _g_kdbus_GetBusId (connection, error); + } + else + { + result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "GetId", + NULL, G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); + } + + if (result != NULL) + { + g_variant_get (result, "(s)", &bus_id); + g_variant_unref (result); + } + + return bus_id; +} + +typedef enum +{ + LIST_NAMES, + LIST_ACTIVATABLE_NAMES, + LIST_QUEUED_OWNERS +} GDBusListNameType; + +static gchar ** +_g_dbus_get_list_internal (GDBusConnection *connection, + const gchar *name, + GDBusListNameType list_name_type, + GError **error) +{ + gchar **strv; + GVariant *result; + GVariantIter *iter; + gchar *str; + gsize n, i; + + result = NULL; + strv = NULL; + + if (list_name_type == LIST_QUEUED_OWNERS) + { + if (G_IS_KDBUS_CONNECTION (connection->stream)) + result = _g_kdbus_GetListQueuedOwners (connection, name, error); + else + result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "ListQueuedOwners", + g_variant_new ("(s)", name), G_VARIANT_TYPE ("(as)"), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); + } + else + { + gchar *method_name; + + if (list_name_type == LIST_NAMES) + method_name = "ListNames"; + else + method_name = "ListActivatableNames"; + + if (G_IS_KDBUS_CONNECTION (connection->stream)) + result = _g_kdbus_GetListNames (connection, list_name_type, error); + else + result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/", + "org.freedesktop.DBus", method_name, + NULL, G_VARIANT_TYPE ("(as)"), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); + } + + if (result != NULL) + { + g_variant_get (result, "(as)", &iter); + n = g_variant_iter_n_children (iter); + strv = g_new (gchar *, n + 1); + + i = 0; + while (g_variant_iter_loop (iter, "s", &str)) + { + strv[i] = g_strdup (str); + i++; + } + strv[i] = NULL; + + g_variant_iter_free (iter); + g_variant_unref (result); + } + + return strv; +} + +/** + * g_dbus_get_list_names: + * @connection: a #GDBusConnection + * @error: return location for error or %NULL + * + * Synchronously returns a list of all currently-owned names on the bus. + * + * The calling thread is blocked until a reply is received. + * + * Returns: a list of all currently-owned names on the bus or %NULL if + * @error is set. Free with g_strfreev(). + * + * Since: 2.4x + */ +gchar ** +g_dbus_get_list_names (GDBusConnection *connection, + GError **error) +{ + gchar **strv; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + strv = _g_dbus_get_list_internal (connection, NULL, LIST_NAMES, error); + + return strv; +} + +/** + * g_dbus_get_list_activatable_names: + * @connection: a #GDBusConnection + * @error: return location for error or %NULL + * + * Synchronously returns a list of all names that can be activated on the bus. + * + * The calling thread is blocked until a reply is received. + * + * Returns: a list of all names that can be activated on the bus or %NULL if + * @error is set. Free with g_strfreev(). + * + * Since: 2.4x + */ +gchar ** +g_dbus_get_list_activatable_names (GDBusConnection *connection, + GError **error) +{ + gchar **strv; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + strv = _g_dbus_get_list_internal (connection, NULL, LIST_ACTIVATABLE_NAMES, error); + + return strv; +} + +/** + * g_dbus_get_list_queued_names: + * @connection: a #GDBusConnection + * @name: a unique or well-known bus name + * @error: return location for error or %NULL + * + * Synchronously returns the unique bus names of connections currently queued + * for the @name. + * + * If @name contains a value not compatible with the D-Bus syntax and naming + * conventions for bus names, the operation returns %NULL and @error is set. + * + * The calling thread is blocked until a reply is received. + * + * Returns: the unique bus names of connections currently queued for the @name + * or %NULL if @error is set. Free with g_strfreev(). + * + * Since: 2.4x + */ +gchar ** +g_dbus_get_list_queued_owners (GDBusConnection *connection, + const gchar *name, + GError **error) +{ + gchar **strv; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (name == NULL || g_dbus_is_name (name), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + strv = _g_dbus_get_list_internal (connection, name, LIST_QUEUED_OWNERS, error); + + return strv; +} + +/** + * g_dbus_get_name_owner: + * @connection: a #GDBusConnection + * @name: well-known bus name to get the owner of + * @error: return location for error or %NULL + * + * Synchronously returns the unique connection name of the primary owner of + * the name given. If the requested name doesn't have an owner, an @error is + * returned. + * + * If @name contains a value not compatible with the D-Bus syntax and naming + * conventions for bus names, the operation returns %NULL and @error is set. + * + * The calling thread is blocked until a reply is received. + * + * Returns: the unique connection name of the primary owner of the + * name given. If the requested name doesn't have an owner, function + * returns %NULL and @error is set. Free with g_free(). + * + * Since: 2.4x + */ +gchar * +g_dbus_get_name_owner (GDBusConnection *connection, + const gchar *name, + GError **error) +{ + GVariant *result; + gchar *name_owner; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (name == NULL || g_dbus_is_name (name), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + name_owner = NULL; + result = NULL; + + if (G_IS_KDBUS_CONNECTION (connection->stream)) + result = _g_kdbus_GetNameOwner (connection, name, error); + else + result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "GetNameOwner", + g_variant_new ("(s)", name), G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); + if (result != NULL) + { + g_variant_get (result, "(s)", &name_owner); + g_variant_unref (result); + } + else + name_owner = NULL; + + return name_owner; +} + +/** + * g_dbus_name_has_owner: + * @connection: a #GDBusConnection + * @name: a unique or well-known bus name + * @error: return location for error or %NULL + * + * Synchronously checks if the specified name exists (currently has an owner). + * + * If @name contains a value not compatible with the D-Bus syntax and naming + * conventions for bus names, the operation returns %NULL and @error is set. + * + * The calling thread is blocked until a reply is received. + * + * Returns: %TRUE if specified name exists (currently has an owner) + * If the requested name doesn't have an owner or @error is set, + * function returns %FALSE. + * + * Since: 2.4x + */ +gboolean +g_dbus_name_has_owner (GDBusConnection *connection, + const gchar *name, + GError **error) +{ + GVariant *result; + gboolean ret; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + g_return_val_if_fail (name == NULL || g_dbus_is_name (name), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + result = NULL; + + if (G_IS_KDBUS_CONNECTION (connection->stream)) + result = _g_kdbus_NameHasOwner (connection, name, error); + else + result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "NameHasOwner", + g_variant_new ("(s)", name), G_VARIANT_TYPE ("(b)"), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); + if (result != NULL) + { + g_variant_get (result, "(b)", &ret); + g_variant_unref (result); + } + else + ret = FALSE; + + return ret; +} + +/** + * g_dbus_get_connection_pid: + * @connection: a #GDBusConnection + * @name: a unique or well-known bus name of the connection to query + * @error: return location for error or %NULL + * + * Synchronously returns the Unix process ID of the process connected to the + * bus. If unable to determine it, an @error is returned. + * + * If @name contains a value not compatible with the D-Bus syntax and naming + * conventions for bus names, the operation returns (guint32) -1 and @error + * is set. + * + * The calling thread is blocked until a reply is received. + * + * Returns: the Unix process ID of the process connected to the bus or + * (guint32) -1 if @error is set. + * + * Since: 2.4x + */ +guint32 +g_dbus_get_connection_pid (GDBusConnection *connection, + const gchar *name, + GError **error) +{ + GVariant *result; + guint32 pid; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), -1); + g_return_val_if_fail (name == NULL || g_dbus_is_name (name), -1); + g_return_val_if_fail (error == NULL || *error == NULL, -1); + + result = NULL; + pid = -1; + + if (G_IS_KDBUS_CONNECTION (connection->stream)) + result = _g_kdbus_GetConnectionUnixProcessID (connection, name, error); + else + result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "GetConnectionUnixProcessID", + g_variant_new ("(s)", name), G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); + if (result != NULL) + { + g_variant_get (result, "(u)", &pid); + g_variant_unref (result); + } + + return pid; +} + +/** + * g_dbus_get_connection_uid: + * @connection: a #GDBusConnection + * @name: a unique or well-known bus name of the connection to query + * @error: return location for error or %NULL + * + * Synchronously returns the Unix user ID of the process connected to the + * bus. If unable to determine it, an @error is returned. + * + * If @name contains a value not compatible with the D-Bus syntax and naming + * conventions for bus names, the operation returns (guint32) -1 and @error + * is set. + * + * The calling thread is blocked until a reply is received. + * + * Returns: the Unix user ID of the process connected to the bus or + * (guint32) -1 if @error is set. + * + * Since: 2.4x + */ +guint32 +g_dbus_get_connection_uid (GDBusConnection *connection, + const gchar *name, + GError **error) +{ + GVariant *result; + guint32 uid; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), -1); + g_return_val_if_fail (name == NULL || g_dbus_is_name (name), -1); + g_return_val_if_fail (error == NULL || *error == NULL, -1); + + result = NULL; + uid = -1; + + if (G_IS_KDBUS_CONNECTION (connection->stream)) + result = _g_kdbus_GetConnectionUnixUser (connection, name, error); + else + result = g_dbus_connection_call_sync (connection, "org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "GetConnectionUnixUser", + g_variant_new ("(s)", name), G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); + if (result != NULL) + { + g_variant_get (result, "(u)", &uid); + g_variant_unref (result); + } + + return uid; +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/** + * g_dbus_connection_get_last_serial: + * @connection: a #GDBusConnection + * + * Retrieves the last serial number assigned to a #GDBusMessage on + * the current thread. This includes messages sent via both low-level + * API such as g_dbus_connection_send_message() as well as + * high-level API such as g_dbus_connection_emit_signal(), + * g_dbus_connection_call() or g_dbus_proxy_call(). + * + * Returns: the last used serial or zero when no message has been sent + * within the current thread + * + * Since: 2.34 + */ +guint32 +g_dbus_connection_get_last_serial (GDBusConnection *connection) +{ + guint32 ret; + + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); + + CONNECTION_LOCK (connection); + ret = GPOINTER_TO_UINT (g_hash_table_lookup (connection->map_thread_to_last_serial, + g_thread_self ())); CONNECTION_UNLOCK (connection); return ret; @@ -1382,6 +2139,7 @@ g_dbus_connection_close_sync (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ +/* Can be called by any thread, with the connection lock held */ static gboolean g_dbus_connection_send_message_unlocked (GDBusConnection *connection, GDBusMessage *message, @@ -1407,14 +2165,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, @@ -1454,6 +2213,15 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, if (out_serial != NULL) *out_serial = serial_to_use; + /* store used serial for the current thread */ + /* TODO: watch the thread disposal and remove associated record + * from hashtable + * - see https://bugzilla.gnome.org/show_bug.cgi?id=676825#c7 + */ + g_hash_table_replace (connection->map_thread_to_last_serial, + g_thread_self (), + GUINT_TO_POINTER (serial_to_use)); + if (!(flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL)) g_dbus_message_set_serial (message, serial_to_use); @@ -1474,12 +2242,12 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, /** * g_dbus_connection_send_message: - * @connection: A #GDBusConnection. - * @message: A #GDBusMessage - * @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. + * @connection: a #GDBusConnection + * @message: a #GDBusMessage + * @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. * @@ -1494,24 +2262,24 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection, * %G_IO_ERROR_CLOSED. If @message is not well-formed, * the operation fails with %G_IO_ERROR_INVALID_ARGUMENT. * - * See and for an example of how to use this - * low-level API to send and receive UNIX file descriptors. + * See this [server][gdbus-server] and [client][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. + * transmission, %FALSE if @error is set * * Since: 2.26 */ gboolean -g_dbus_connection_send_message (GDBusConnection *connection, - GDBusMessage *message, - GDBusSendMessageFlags flags, - volatile guint32 *out_serial, - GError **error) +g_dbus_connection_send_message (GDBusConnection *connection, + GDBusMessage *message, + GDBusSendMessageFlags flags, + volatile guint32 *out_serial, + GError **error) { gboolean ret; @@ -1546,6 +2314,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) { @@ -1553,6 +2322,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) { @@ -1564,8 +2334,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); } } @@ -1574,7 +2343,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); @@ -1597,15 +2366,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) @@ -1617,7 +2389,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: ; @@ -1625,6 +2397,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) { @@ -1639,7 +2412,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); @@ -1663,12 +2436,14 @@ send_message_with_reply_cancelled_cb (GCancellable *cancellable, send_message_with_reply_cancelled_idle_cb, send_message_data_ref (data), (GDestroyNotify) send_message_data_unref); + g_source_set_name (idle_source, "[gio] send_message_with_reply_cancelled_idle_cb"); g_source_attach (idle_source, data->main_context); g_source_unref (idle_source); } /* ---------------------------------------------------------------------------------------------------- */ +/* Called from a user thread, lock is not held */ static gboolean send_message_with_reply_timeout_cb (gpointer user_data) { @@ -1683,7 +2458,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); @@ -1693,6 +2468,7 @@ 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, @@ -1720,6 +2496,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)) { @@ -1732,17 +2509,6 @@ 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, flags, out_serial, &error)) { @@ -1757,9 +2523,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) { @@ -1768,10 +2532,6 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect G_CALLBACK (send_message_with_reply_cancelled_cb), send_message_data_ref (data), (GDestroyNotify) send_message_data_unref); - g_object_set_data_full (G_OBJECT (simple), - "cancellable", - g_object_ref (cancellable), - (GDestroyNotify) g_object_unref); } if (timeout_msec != G_MAXINT) @@ -1796,17 +2556,17 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect /** * g_dbus_connection_send_message_with_reply: - * @connection: A #GDBusConnection. - * @message: A #GDBusMessage. - * @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: (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. + * @connection: a #GDBusConnection + * @message: a #GDBusMessage + * @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: (allow-none): 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 + * @user_data: The data to pass to @callback * * Asynchronously sends @message to the peer represented by @connection. * @@ -1822,8 +2582,9 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect * fail with %G_IO_ERROR_CANCELLED. If @message is not well-formed, * the operation fails with %G_IO_ERROR_INVALID_ARGUMENT. * - * This is an asynchronous method. When the operation is finished, @callback will be invoked - * in the thread-default main loop + * This is an asynchronous method. When the operation is finished, @callback + * will be invoked in the + * [thread-default main context][g-main-context-push-thread-default] * of the thread you are calling this method from. You can then call * 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. @@ -1831,21 +2592,21 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection *connect * 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. + * See this [server][gdbus-server] and [client][gdbus-unix-fd-client] + * for an example of how to use this low-level API to send and receive + * UNIX file descriptors. * * Since: 2.26 */ void -g_dbus_connection_send_message_with_reply (GDBusConnection *connection, - GDBusMessage *message, - GDBusSendMessageFlags flags, - gint timeout_msec, - volatile guint32 *out_serial, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +g_dbus_connection_send_message_with_reply (GDBusConnection *connection, + GDBusMessage *message, + GDBusSendMessageFlags flags, + gint timeout_msec, + volatile guint32 *out_serial, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); g_return_if_fail (G_IS_DBUS_MESSAGE (message)); @@ -1867,8 +2628,9 @@ g_dbus_connection_send_message_with_reply (GDBusConnection *connection, /** * g_dbus_connection_send_message_with_reply_finish: * @connection: a #GDBusConnection - * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_send_message_with_reply(). - * @error: Return location for error or %NULL. + * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed to + * g_dbus_connection_send_message_with_reply() + * @error: teturn location for error or %NULL * * Finishes an operation started with g_dbus_connection_send_message_with_reply(). * @@ -1877,11 +2639,11 @@ g_dbus_connection_send_message_with_reply (GDBusConnection *connection, * be of type %G_DBUS_MESSAGE_TYPE_ERROR. Use * g_dbus_message_to_gerror() to transcode this to a #GError. * - * See and for an example of how to use this - * low-level API to send and receive UNIX file descriptors. + * See this [server][gdbus-server] and [client][gdbus-unix-fd-client] + * for an example of how to use this low-level API to send and receive + * UNIX file descriptors. * - * Returns: (transfer full): A locked #GDBusMessage or %NULL if @error is set. + * Returns: (transfer full): a locked #GDBusMessage or %NULL if @error is set * * Since: 2.26 */ @@ -1892,7 +2654,6 @@ g_dbus_connection_send_message_with_reply_finish (GDBusConnection *connection, { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); GDBusMessage *reply; - GCancellable *cancellable; g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); @@ -1905,16 +2666,7 @@ g_dbus_connection_send_message_with_reply_finish (GDBusConnection *connection, goto out; reply = g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); - cancellable = g_object_get_data (G_OBJECT (simple), "cancellable"); - if (cancellable != NULL && g_cancellable_is_cancelled (cancellable)) - { - g_object_unref (reply); - reply = NULL; - g_set_error_literal (error, - G_IO_ERROR, - G_IO_ERROR_CANCELLED, - _("Operation was cancelled")); - } + out: return reply; } @@ -1928,6 +2680,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, @@ -1940,15 +2693,15 @@ send_message_with_reply_sync_cb (GDBusConnection *connection, /** * g_dbus_connection_send_message_with_reply_sync: - * @connection: A #GDBusConnection. - * @message: A #GDBusMessage. - * @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. + * @connection: a #GDBusConnection + * @message: a #GDBusMessage + * @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: (allow-none): a #GCancellable or %NULL + * @error: return location for error or %NULL * * Synchronously sends @message to the peer represented by @connection * and blocks the calling thread until a reply is received or the @@ -1972,25 +2725,26 @@ send_message_with_reply_sync_cb (GDBusConnection *connection, * be of type %G_DBUS_MESSAGE_TYPE_ERROR. Use * g_dbus_message_to_gerror() to transcode this to a #GError. * - * See and for an example of how to use this - * low-level API to send and receive UNIX file descriptors. + * See this [server][gdbus-server] and [client][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: (transfer full): A locked #GDBusMessage that is the reply to @message or %NULL if @error is set. + * 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, - GError **error) +g_dbus_connection_send_message_with_reply_sync (GDBusConnection *connection, + GDBusMessage *message, + GDBusSendMessageFlags flags, + gint timeout_msec, + volatile guint32 *out_serial, + GCancellable *cancellable, + GError **error) { SendMessageSyncData *data; GDBusMessage *reply; @@ -2046,7 +2800,7 @@ 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, @@ -2148,7 +2902,7 @@ on_worker_message_received (GDBusWorker *worker, g_free (filters); } -/* Called in worker's thread */ +/* Called in GDBusWorker's thread, lock is not held */ static GDBusMessage * on_worker_message_about_to_be_sent (GDBusWorker *worker, GDBusMessage *message, @@ -2203,7 +2957,29 @@ on_worker_message_about_to_be_sent (GDBusWorker *worker, return message; } -/* Called in worker's thread - we must not block */ +/* 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 GDBusWorker's thread - we must not block - without lock held */ static void on_worker_closed (GDBusWorker *worker, gboolean remote_peer_vanished, @@ -2212,6 +2988,7 @@ on_worker_closed (GDBusWorker *worker, { GDBusConnection *connection; gboolean alive; + guint old_atomic_flags; G_LOCK (message_bus_lock); alive = (g_hash_table_lookup (alive_connections, user_data) != NULL); @@ -2227,8 +3004,17 @@ on_worker_closed (GDBusWorker *worker, //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); @@ -2236,7 +3022,11 @@ on_worker_closed (GDBusWorker *worker, /* ---------------------------------------------------------------------------------------------------- */ -/* 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) { @@ -2249,6 +3039,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, @@ -2265,24 +3056,21 @@ 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; - /* First, handle the case where the connection already has an - * initialization error set. + /* Make this a no-op if we're already initialized (successfully or + * unsuccessfully) */ - if (connection->initialization_error != NULL) - goto out; - - /* Also make this a no-op if we're already initialized fine */ - if (connection->is_initialized) + if ((g_atomic_int_get (&connection->atomic_flags) & FLAG_INITIALIZED)) { - ret = TRUE; + ret = (connection->initialization_error == NULL); goto out; } - g_assert (connection->initialization_error == NULL && !connection->is_initialized); + /* 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 * properties: @@ -2300,7 +3088,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")); @@ -2323,6 +3111,12 @@ initable_init (GInitable *initable, g_assert_not_reached (); } + /* [KDBUS] Skip authentication process for kdbus transport */ + if (G_IS_KDBUS_CONNECTION (connection->stream)) + { + goto authenticated; + } + /* Authenticate the connection */ if (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER) { @@ -2346,6 +3140,7 @@ initable_init (GInitable *initable, g_assert (connection->guid == NULL); connection->auth = _g_dbus_auth_new (connection->stream); connection->guid = _g_dbus_auth_run_client (connection->auth, + connection->authentication_observer, get_offered_capabilities_max (connection), &connection->capabilities, cancellable, @@ -2360,6 +3155,8 @@ initable_init (GInitable *initable, connection->authentication_observer = NULL; } +authenticated: + //g_output_stream_flush (G_SOCKET_CONNECTION (connection->stream) //g_debug ("haz unix fd passing powers: %d", connection->capabilities & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING); @@ -2382,7 +3179,7 @@ initable_init (GInitable *initable, 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, @@ -2403,27 +3200,33 @@ initable_init (GInitable *initable, goto out; } - hello_result = g_dbus_connection_call_sync (connection, - "org.freedesktop.DBus", /* name */ - "/org/freedesktop/DBus", /* path */ - "org.freedesktop.DBus", /* interface */ - "Hello", - NULL, /* parameters */ - G_VARIANT_TYPE ("(s)"), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, /* TODO: cancellable */ - &connection->initialization_error); + if (G_IS_KDBUS_CONNECTION (connection->stream)) + { + hello_result = _g_kdbus_Hello (connection->stream, &connection->initialization_error); + } + else + { + hello_result = g_dbus_connection_call_sync (connection, + "org.freedesktop.DBus", /* name */ + "/org/freedesktop/DBus", /* path */ + "org.freedesktop.DBus", /* interface */ + "Hello", + NULL, /* parameters */ + G_VARIANT_TYPE ("(s)"), + CALL_FLAGS_INITIALIZING, + -1, + NULL, /* TODO: cancellable */ + &connection->initialization_error); + } + if (hello_result == NULL) goto out; g_variant_get (hello_result, "(s)", &connection->bus_unique_name); g_variant_unref (hello_result); - //g_debug ("unique name is `%s'", connection->bus_unique_name); + //g_debug ("unique name is '%s'", connection->bus_unique_name); } - connection->is_initialized = TRUE; - ret = TRUE; out: if (!ret) @@ -2432,7 +3235,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; } @@ -2455,13 +3259,13 @@ async_initable_iface_init (GAsyncInitableIface *async_initable_iface) /** * g_dbus_connection_new: - * @stream: A #GIOStream. - * @guid: (allow-none): The GUID to use if a authenticating as a server or %NULL. - * @flags: Flags describing how to make the connection. - * @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. + * @stream: a #GIOStream + * @guid: (allow-none): the GUID to use if a authenticating as a server or %NULL + * @flags: flags describing how to make the connection + * @observer: (allow-none): a #GDBusAuthObserver or %NULL + * @cancellable: (allow-none): a #GCancellable or %NULL + * @callback: a #GAsyncReadyCallback to call when the request is satisfied + * @user_data: the data to pass to @callback * * Asynchronously sets up a D-Bus connection for exchanging D-Bus messages * with the end represented by @stream. @@ -2469,6 +3273,10 @@ async_initable_iface_init (GAsyncInitableIface *async_initable_iface) * 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. * @@ -2506,12 +3314,14 @@ g_dbus_connection_new (GIOStream *stream, /** * g_dbus_connection_new_finish: - * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_new(). - * @error: Return location for error or %NULL. + * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback + * passed to g_dbus_connection_new(). + * @error: return location for error or %NULL * * Finishes an operation started with g_dbus_connection_new(). * - * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + * Returns: a #GDBusConnection or %NULL if @error is set. Free + * with g_object_unref(). * * Since: 2.26 */ @@ -2539,12 +3349,12 @@ g_dbus_connection_new_finish (GAsyncResult *res, /** * g_dbus_connection_new_sync: - * @stream: A #GIOStream. - * @guid: (allow-none): The GUID to use if a authenticating as a server or %NULL. - * @flags: Flags describing how to make the connection. - * @observer: (allow-none): A #GDBusAuthObserver or %NULL. - * @cancellable: A #GCancellable or %NULL. - * @error: Return location for error or %NULL. + * @stream: a #GIOStream + * @guid: (allow-none): the GUID to use if a authenticating as a server or %NULL + * @flags: flags describing how to make the connection + * @observer: (allow-none): a #GDBusAuthObserver or %NULL + * @cancellable: (allow-none): 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. @@ -2552,13 +3362,17 @@ g_dbus_connection_new_finish (GAsyncResult *res, * 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. * * This is a synchronous failable constructor. See * g_dbus_connection_new() for the asynchronous version. * - * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + * Returns: a #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). * * Since: 2.26 */ @@ -2586,12 +3400,12 @@ 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: (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. + * @address: a D-Bus address + * @flags: flags describing how to make the connection + * @observer: (allow-none): a #GDBusAuthObserver or %NULL + * @cancellable: (allow-none): a #GCancellable or %NULL + * @callback: a #GAsyncReadyCallback to call when the request is satisfied + * @user_data: the data to pass to @callback * * Asynchronously connects and sets up a D-Bus client connection for * exchanging D-Bus messages with an endpoint specified by @address @@ -2638,12 +3452,14 @@ g_dbus_connection_new_for_address (const gchar *address, /** * g_dbus_connection_new_for_address_finish: - * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_new(). - * @error: Return location for error or %NULL. + * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed + * to g_dbus_connection_new() + * @error: return location for error or %NULL * * Finishes an operation started with g_dbus_connection_new_for_address(). * - * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + * Returns: a #GDBusConnection or %NULL if @error is set. Free with + * g_object_unref(). * * Since: 2.26 */ @@ -2671,11 +3487,11 @@ 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: (allow-none): A #GDBusAuthObserver or %NULL. - * @cancellable: A #GCancellable or %NULL. - * @error: Return location for error or %NULL. + * @address: a D-Bus address + * @flags: flags describing how to make the connection + * @observer: (allow-none): a #GDBusAuthObserver or %NULL + * @cancellable: (allow-none): a #GCancellable or %NULL + * @error: return location for error or %NULL * * Synchronously connects and sets up a D-Bus client connection for * exchanging D-Bus messages with an endpoint specified by @address @@ -2693,7 +3509,8 @@ g_dbus_connection_new_for_address_finish (GAsyncResult *res, * If @observer is not %NULL it may be used to control the * authentication process. * - * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref(). + * Returns: a #GDBusConnection or %NULL if @error is set. Free with + * g_object_unref(). * * Since: 2.26 */ @@ -2719,9 +3536,9 @@ 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. + * @connection: a #GDBusConnection + * @exit_on_close: whether the process should be terminated + * 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 @@ -2741,19 +3558,24 @@ 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); + } /** * g_dbus_connection_get_exit_on_close: - * @connection: A #GDBusConnection. + * @connection: a #GDBusConnection * * Gets whether the process is terminated when @connection is * closed by the remote peer. See * #GDBusConnection:exit-on-close for more details. * - * Returns: Whether the process is terminated when @connection is - * closed by the remote peer. + * Returns: whether the process is terminated when @connection is + * closed by the remote peer * * Since: 2.26 */ @@ -2761,18 +3583,22 @@ 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; } /** * g_dbus_connection_get_guid: - * @connection: A #GDBusConnection. + * @connection: a #GDBusConnection * * The GUID of the peer performing the role of server when * authenticating. See #GDBusConnection:guid for more details. * * Returns: The GUID. Do not free this string, it is owned by - * @connection. + * @connection. * * Since: 2.26 */ @@ -2785,15 +3611,15 @@ g_dbus_connection_get_guid (GDBusConnection *connection) /** * g_dbus_connection_get_unique_name: - * @connection: A #GDBusConnection. + * @connection: a #GDBusConnection * * Gets the unique name of @connection as assigned by the message * bus. This can also be used to figure out if @connection is a * message bus connection. * - * Returns: The unique name or %NULL if @connection is not a message - * bus connection. Do not free this string, it is owned by - * @connection. + * Returns: the unique name or %NULL if @connection is not a message + * bus connection. Do not free this string, it is owned by + * @connection. * * Since: 2.26 */ @@ -2801,12 +3627,17 @@ 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; } /** * g_dbus_connection_get_peer_credentials: - * @connection: A #GDBusConnection. + * @connection: a #GDBusConnection * * Gets the credentials of the authenticated peer. This will always * return %NULL unless @connection acted as a server @@ -2818,8 +3649,8 @@ 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: (transfer none): A #GCredentials or %NULL if not available. Do not free - * this object, it is owned by @connection. + * Returns: (transfer none): a #GCredentials or %NULL if not available. + * Do not free this object, it is owned by @connection. * * Since: 2.26 */ @@ -2827,6 +3658,11 @@ GCredentials * g_dbus_connection_get_peer_credentials (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->credentials; } @@ -2836,11 +3672,11 @@ static guint _global_filter_id = 1; /** * g_dbus_connection_add_filter: - * @connection: A #GDBusConnection. - * @filter_function: A filter function. - * @user_data: User data to pass to @filter_function. - * @user_data_free_func: Function to free @user_data with when filter - * is removed or %NULL. + * @connection: a #GDBusConnection + * @filter_function: a filter function + * @user_data: user data to pass to @filter_function + * @user_data_free_func: function to free @user_data with when filter + * is removed or %NULL * * Adds a message filter. Filters are handlers that are run on all * incoming and outgoing messages, prior to standard dispatch. Filters @@ -2863,8 +3699,8 @@ static guint _global_filter_id = 1; * message. Similary, if a filter consumes an outgoing message, the * message will not be sent to the other peer. * - * Returns: A filter identifier that can be used with - * g_dbus_connection_remove_filter(). + * Returns: a filter identifier that can be used with + * g_dbus_connection_remove_filter() * * Since: 2.26 */ @@ -2878,6 +3714,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); @@ -2922,6 +3759,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; @@ -2961,6 +3799,7 @@ typedef struct gchar *member; gchar *object_path; gchar *arg0; + GDBusSignalFlags flags; GArray *subscribers; } SignalData; @@ -2988,17 +3827,17 @@ signal_data_free (SignalData *signal_data) } static gchar * -args_to_rule (const gchar *sender, - const gchar *interface_name, - const gchar *member, - const gchar *object_path, - const gchar *arg0, - gboolean negate) +args_to_rule (const gchar *sender, + const gchar *interface_name, + const gchar *member, + const gchar *object_path, + const gchar *arg0, + GDBusSignalFlags flags) { GString *rule; rule = g_string_new ("type='signal'"); - if (negate) + if (flags & G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE) g_string_prepend_c (rule, '-'); if (sender != NULL) g_string_append_printf (rule, ",sender='%s'", sender); @@ -3008,8 +3847,16 @@ args_to_rule (const gchar *sender, g_string_append_printf (rule, ",member='%s'", member); if (object_path != NULL) g_string_append_printf (rule, ",path='%s'", object_path); + if (arg0 != NULL) - g_string_append_printf (rule, ",arg0='%s'", arg0); + { + if (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH) + g_string_append_printf (rule, ",arg0path='%s'", arg0); + else if (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE) + g_string_append_printf (rule, ",arg0namespace='%s'", arg0); + else + g_string_append_printf (rule, ",arg0='%s'", arg0); + } return g_string_free (rule, FALSE); } @@ -3020,7 +3867,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) @@ -3051,7 +3898,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) @@ -3075,6 +3922,10 @@ remove_match_rule (GDBusConnection *connection, 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); } @@ -3097,26 +3948,27 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) /** * g_dbus_connection_signal_subscribe: - * @connection: A #GDBusConnection. - * @sender: (allow-none): Sender name to match on (unique or well-known name) - * or %NULL to listen from all senders. + * @connection: a #GDBusConnection + * @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: (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 - * will be invoked in the thread-default main - * loop of the thread you are calling this method from. + * 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: (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 will be invoked in the + * [thread-default main context][g-main-context-push-thread-default] + * of the thread you are calling this method from. * * If @connection is not a message bus connection, @sender must be * %NULL. @@ -3128,7 +3980,12 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) * tracking the name owner of the well-known name and use that when * processing the received signal. * - * Returns: A subscription identifier that can be used with g_dbus_connection_signal_unsubscribe(). + * If one of %G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE or + * %G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH are given, @arg0 is + * interpreted as part of a namespace or path. The first argument + * of a signal is matched against that part as specified by D-Bus. + * + * Returns: a subscription identifier that can be used with g_dbus_connection_signal_unsubscribe() * * Since: 2.26 */ @@ -3166,6 +4023,9 @@ 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); + g_return_val_if_fail (!((flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH) && (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE)), 0); + g_return_val_if_fail (!(arg0 == NULL && (flags & (G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH | G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE))), 0); CONNECTION_LOCK (connection); @@ -3177,8 +4037,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, * 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); + rule = args_to_rule (sender, interface_name, member, object_path, arg0, flags); if (sender != NULL && (g_dbus_is_unique_name (sender) || g_strcmp0 (sender, "org.freedesktop.DBus") == 0)) sender_unique_name = sender; @@ -3189,9 +4048,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); @@ -3210,6 +4067,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, signal_data->member = g_strdup (member); signal_data->object_path = g_strdup (object_path); signal_data->arg0 = g_strdup (arg0); + signal_data->flags = flags; signal_data->subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber)); g_array_append_val (signal_data->subscribers, subscriber); @@ -3226,6 +4084,16 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, { if (!is_signal_data_for_name_lost_or_acquired (signal_data)) add_match_rule (connection, signal_data->rule); + else + { + if (G_IS_KDBUS_CONNECTION (connection->stream)) + { + if (g_strcmp0 (signal_data->member, "NameAcquired") == 0) + _g_kdbus_subscribe_name_acquired (connection, arg0); + else if (g_strcmp0 (signal_data->member, "NameLost") == 0) + _g_kdbus_subscribe_name_lost (connection, arg0); + } + } } signal_data_array = g_hash_table_lookup (connection->map_sender_unique_name_to_signal_data_array, @@ -3251,6 +4119,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ +/* called in any thread */ /* must hold lock when calling this (except if connection->finalizing is TRUE) */ static void unsubscribe_id_internal (GDBusConnection *connection, @@ -3298,12 +4167,27 @@ 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) + { + /* 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); + } + else { - if (!is_signal_data_for_name_lost_or_acquired (signal_data)) - if (!connection->closed && !connection->finalizing) - remove_match_rule (connection, signal_data->rule); + if (G_IS_KDBUS_CONNECTION (connection->stream)) + { + //_g_kdbus_unsubscribe_name_lost_and_acquired (connection, arg0); + } } + signal_data_free (signal_data); } @@ -3318,8 +4202,9 @@ unsubscribe_id_internal (GDBusConnection *connection, /** * g_dbus_connection_signal_unsubscribe: - * @connection: A #GDBusConnection. - * @subscription_id: A subscription id obtained from g_dbus_connection_signal_subscribe(). + * @connection: a #GDBusConnection + * @subscription_id: a subscription id obtained from + * g_dbus_connection_signal_subscribe() * * Unsubscribes from signals. * @@ -3333,6 +4218,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)); @@ -3353,8 +4239,7 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection, call_destroy_notify (subscriber->context, subscriber->user_data_free_func, subscriber->user_data); - if (subscriber->context != NULL) - g_main_context_unref (subscriber->context); + g_main_context_unref (subscriber->context); } g_array_free (subscribers, TRUE); @@ -3393,7 +4278,7 @@ emit_signal_instance_in_idle_cb (gpointer data) } else { - g_variant_ref_sink (parameters); + g_variant_ref (parameters); } #if 0 @@ -3436,7 +4321,44 @@ signal_instance_free (SignalInstance *signal_instance) g_free (signal_instance); } -/* called in message handler thread WITH lock held */ +static gboolean +namespace_rule_matches (const gchar *namespace, + const gchar *name) +{ + gint len_namespace; + gint len_name; + + len_namespace = strlen (namespace); + len_name = strlen (name); + + if (len_name < len_namespace) + return FALSE; + + if (memcmp (namespace, name, len_namespace) != 0) + return FALSE; + + return len_namespace == len_name || name[len_namespace] == '.'; +} + +static gboolean +path_rule_matches (const gchar *path_a, + const gchar *path_b) +{ + gint len_a, len_b; + + len_a = strlen (path_a); + len_b = strlen (path_b); + + if (len_a < len_b && path_a[len_a - 1] != '/') + return FALSE; + + if (len_b < len_a && path_b[len_b - 1] != '/') + return FALSE; + + return memcmp (path_a, path_b, MIN (len_a, len_b)) == 0; +} + +/* called in GDBusWorker thread WITH lock held */ static void schedule_callbacks (GDBusConnection *connection, GPtrArray *signal_data_array, @@ -3461,11 +4383,11 @@ schedule_callbacks (GDBusConnection *connection, #if 0 g_print ("In schedule_callbacks:\n" - " sender = `%s'\n" - " interface = `%s'\n" - " member = `%s'\n" - " path = `%s'\n" - " arg0 = `%s'\n", + " sender = '%s'\n" + " interface = '%s'\n" + " member = '%s'\n" + " path = '%s'\n" + " arg0 = '%s'\n", sender, interface, member, @@ -3489,8 +4411,24 @@ schedule_callbacks (GDBusConnection *connection, if (signal_data->object_path != NULL && g_strcmp0 (signal_data->object_path, path) != 0) continue; - if (signal_data->arg0 != NULL && g_strcmp0 (signal_data->arg0, arg0) != 0) - continue; + if (signal_data->arg0 != NULL) + { + if (arg0 == NULL) + continue; + + if (signal_data->flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE) + { + if (!namespace_rule_matches (signal_data->arg0, arg0)) + continue; + } + else if (signal_data->flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH) + { + if (!path_rule_matches (signal_data->arg0, arg0)) + continue; + } + else if (!g_str_equal (signal_data->arg0, arg0)) + continue; + } for (m = 0; m < signal_data->subscribers->len; m++) { @@ -3517,13 +4455,14 @@ schedule_callbacks (GDBusConnection *connection, emit_signal_instance_in_idle_cb, signal_instance, (GDestroyNotify) signal_instance_free); + g_source_set_name (idle_source, "[gio] emit_signal_instance_in_idle_cb"); g_source_attach (idle_source, subscriber->context); g_source_unref (idle_source); } } } -/* called in message handler thread with lock held */ +/* called in GDBusWorker thread with lock held */ static void distribute_signals (GDBusConnection *connection, GDBusMessage *message) @@ -3600,8 +4539,7 @@ purge_all_signal_subscriptions (GDBusConnection *connection) call_destroy_notify (subscriber->context, subscriber->user_data_free_func, subscriber->user_data); - if (subscriber->context != NULL) - g_main_context_unref (subscriber->context); + g_main_context_unref (subscriber->context); } g_array_free (subscribers, TRUE); @@ -3686,8 +4624,7 @@ exported_interface_free (ExportedInterface *ei) 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); @@ -3700,7 +4637,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, @@ -3767,7 +4704,7 @@ invoke_get_property_in_idle_cb (gpointer _data) { reply = g_dbus_message_new_method_error (data->message, "org.freedesktop.DBus.Error.UnknownMethod", - _("No such interface `org.freedesktop.DBus.Properties' on object at path %s"), + _("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, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); @@ -3831,20 +4768,6 @@ invoke_set_property_in_idle_cb (gpointer _data) NULL, &value); - /* Fail with org.freedesktop.DBus.Error.InvalidArgs if the type - * of the given value is wrong - */ - if (g_strcmp0 (g_variant_get_type_string (value), data->property_info->signature) != 0) - { - reply = g_dbus_message_new_method_error (data->message, - "org.freedesktop.DBus.Error.InvalidArgs", - _("Error setting property `%s': Expected type `%s' but got `%s'"), - data->property_info->name, - data->property_info->signature, - g_variant_get_type_string (value)); - goto out; - } - if (!data->vtable->set_property (data->connection, g_dbus_message_get_sender (data->message), g_dbus_message_get_path (data->message), @@ -3868,7 +4791,6 @@ invoke_set_property_in_idle_cb (gpointer _data) reply = g_dbus_message_new_method_reply (data->message); } - out: g_assert (reply != NULL); g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); @@ -3877,7 +4799,7 @@ invoke_set_property_in_idle_cb (gpointer _data) 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, @@ -3911,17 +4833,8 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect &property_name, NULL); - - if (is_get) - { - if (vtable == NULL || vtable->get_property == NULL) - goto out; - } - else - { - if (vtable == NULL || vtable->set_property == NULL) - goto out; - } + if (vtable == NULL) + goto out; /* Check that the property exists - if not fail with org.freedesktop.DBus.Error.InvalidArgs */ @@ -3933,7 +4846,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect { reply = g_dbus_message_new_method_error (message, "org.freedesktop.DBus.Error.InvalidArgs", - _("No such property `%s'"), + _("No such property '%s'"), property_name); g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); @@ -3945,7 +4858,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect { reply = g_dbus_message_new_method_error (message, "org.freedesktop.DBus.Error.InvalidArgs", - _("Property `%s' is not readable"), + _("Property '%s' is not readable"), property_name); g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); @@ -3956,7 +4869,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect { reply = g_dbus_message_new_method_error (message, "org.freedesktop.DBus.Error.InvalidArgs", - _("Property `%s' is not writable"), + _("Property '%s' is not writable"), property_name); g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); @@ -3964,6 +4877,57 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect goto out; } + if (!is_get) + { + GVariant *value; + + /* Fail with org.freedesktop.DBus.Error.InvalidArgs if the type + * of the given value is wrong + */ + g_variant_get_child (g_dbus_message_get_body (message), 2, "v", &value); + if (g_strcmp0 (g_variant_get_type_string (value), property_info->signature) != 0) + { + reply = g_dbus_message_new_method_error (message, + "org.freedesktop.DBus.Error.InvalidArgs", + _("Error setting property '%s': Expected type '%s' but got '%s'"), + property_name, property_info->signature, + g_variant_get_type_string (value)); + g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); + g_variant_unref (value); + g_object_unref (reply); + handled = TRUE; + goto out; + } + + g_variant_unref (value); + } + + /* If the vtable pointer for get_property() resp. set_property() is + * NULL then dispatch the call via the method_call() handler. + */ + if (is_get) + { + if (vtable->get_property == NULL) + { + schedule_method_call (connection, message, registration_id, subtree_registration_id, + interface_info, NULL, property_info, g_dbus_message_get_body (message), + vtable, main_context, user_data); + handled = TRUE; + goto out; + } + } + else + { + if (vtable->set_property == NULL) + { + schedule_method_call (connection, message, registration_id, subtree_registration_id, + interface_info, NULL, property_info, g_dbus_message_get_body (message), + vtable, main_context, user_data); + handled = TRUE; + goto out; + } + } + /* ok, got the property info - call user code in an idle handler */ property_data = g_new0 (PropertyData, 1); property_data->connection = g_object_ref (connection); @@ -3982,6 +4946,10 @@ validate_and_maybe_schedule_property_getset (GDBusConnection *connect is_get ? invoke_get_property_in_idle_cb : invoke_set_property_in_idle_cb, property_data, (GDestroyNotify) property_data_free); + if (is_get) + g_source_set_name (idle_source, "[gio] invoke_get_property_in_idle_cb"); + else + g_source_set_name (idle_source, "[gio] invoke_set_property_in_idle_cb"); g_source_attach (idle_source, main_context); g_source_unref (idle_source); @@ -3991,7 +4959,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, @@ -4026,7 +4994,7 @@ handle_getset_property (GDBusConnection *connection, GDBusMessage *reply; reply = g_dbus_message_new_method_error (message, "org.freedesktop.DBus.Error.InvalidArgs", - _("No such interface `%s'"), + _("No such interface '%s'"), interface_name); g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); @@ -4083,7 +5051,7 @@ invoke_get_all_properties_in_idle_cb (gpointer _data) { reply = g_dbus_message_new_method_error (data->message, "org.freedesktop.DBus.Error.UnknownMethod", - _("No such interface `org.freedesktop.DBus.Properties' on object at path %s"), + _("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, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); @@ -4135,7 +5103,22 @@ invoke_get_all_properties_in_idle_cb (gpointer _data) return FALSE; } -/* called with lock held */ +static gboolean +interface_has_readable_properties (GDBusInterfaceInfo *interface_info) +{ + gint i; + + if (!interface_info->properties) + return FALSE; + + for (i = 0; interface_info->properties[i]; i++) + if (interface_info->properties[i]->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE) + return TRUE; + + return FALSE; +} + +/* called in any thread with connection's lock held */ static gboolean validate_and_maybe_schedule_property_get_all (GDBusConnection *connection, GDBusMessage *message, @@ -4147,19 +5130,27 @@ validate_and_maybe_schedule_property_get_all (GDBusConnection *connec gpointer user_data) { gboolean handled; - const char *interface_name; GSource *idle_source; PropertyGetAllData *property_get_all_data; handled = FALSE; - g_variant_get (g_dbus_message_get_body (message), - "(&s)", - &interface_name); - - if (vtable == NULL || vtable->get_property == NULL) + if (vtable == NULL) goto out; + /* If the vtable pointer for get_property() is NULL but we have a + * non-zero number of readable properties, then dispatch the call via + * the method_call() handler. + */ + if (vtable->get_property == NULL && interface_has_readable_properties (interface_info)) + { + schedule_method_call (connection, message, registration_id, subtree_registration_id, + interface_info, NULL, NULL, g_dbus_message_get_body (message), + vtable, main_context, user_data); + handled = TRUE; + goto out; + } + /* ok, got the property info - call user in an idle handler */ property_get_all_data = g_new0 (PropertyGetAllData, 1); property_get_all_data->connection = g_object_ref (connection); @@ -4176,6 +5167,7 @@ validate_and_maybe_schedule_property_get_all (GDBusConnection *connec invoke_get_all_properties_in_idle_cb, property_get_all_data, (GDestroyNotify) property_get_all_data_free); + g_source_set_name (idle_source, "[gio] invoke_get_all_properties_in_idle_cb"); g_source_attach (idle_source, main_context); g_source_unref (idle_source); @@ -4185,7 +5177,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, @@ -4307,6 +5299,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) @@ -4348,6 +5341,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) @@ -4359,7 +5353,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, @@ -4416,9 +5410,6 @@ call_in_idle_cb (gpointer user_data) guint registration_id; guint subtree_registration_id; - vtable = g_object_get_data (G_OBJECT (invocation), "g-dbus-interface-vtable"); - g_assert (vtable != NULL && vtable->method_call != NULL); - registration_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (invocation), "g-dbus-registration-id")); subtree_registration_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (invocation), "g-dbus-subtree-registration-id")); @@ -4429,7 +5420,7 @@ call_in_idle_cb (gpointer user_data) GDBusMessage *reply; reply = g_dbus_message_new_method_error (g_dbus_method_invocation_get_message (invocation), "org.freedesktop.DBus.Error.UnknownMethod", - _("No such interface `%s' on object at path %s"), + _("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, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); @@ -4437,6 +5428,9 @@ call_in_idle_cb (gpointer user_data) goto out; } + vtable = g_object_get_data (G_OBJECT (invocation), "g-dbus-interface-vtable"); + g_assert (vtable != NULL && vtable->method_call != NULL); + vtable->method_call (g_dbus_method_invocation_get_connection (invocation), g_dbus_method_invocation_get_sender (invocation), g_dbus_method_invocation_get_object_path (invocation), @@ -4450,7 +5444,52 @@ 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 void +schedule_method_call (GDBusConnection *connection, + GDBusMessage *message, + guint registration_id, + guint subtree_registration_id, + const GDBusInterfaceInfo *interface_info, + const GDBusMethodInfo *method_info, + const GDBusPropertyInfo *property_info, + GVariant *parameters, + const GDBusInterfaceVTable *vtable, + GMainContext *main_context, + gpointer user_data) +{ + GDBusMethodInvocation *invocation; + GSource *idle_source; + + 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, + property_info, + connection, + message, + parameters, + user_data); + + /* TODO: would be nicer with a real MethodData like we already + * have PropertyData and PropertyGetAllData... */ + g_object_set_data (G_OBJECT (invocation), "g-dbus-interface-vtable", (gpointer) vtable); + g_object_set_data (G_OBJECT (invocation), "g-dbus-registration-id", GUINT_TO_POINTER (registration_id)); + g_object_set_data (G_OBJECT (invocation), "g-dbus-subtree-registration-id", GUINT_TO_POINTER (subtree_registration_id)); + + idle_source = g_idle_source_new (); + g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); + g_source_set_callback (idle_source, + call_in_idle_cb, + invocation, + g_object_unref); + g_source_set_name (idle_source, "[gio] call_in_idle_cb"); + g_source_attach (idle_source, main_context); + g_source_unref (idle_source); +} + +/* called in GDBusWorker thread with connection's lock held */ static gboolean validate_and_maybe_schedule_method_call (GDBusConnection *connection, GDBusMessage *message, @@ -4461,11 +5500,9 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection, GMainContext *main_context, gpointer user_data) { - GDBusMethodInvocation *invocation; - const GDBusMethodInfo *method_info; + GDBusMethodInfo *method_info; GDBusMessage *reply; GVariant *parameters; - GSource *idle_source; gboolean handled; GVariantType *in_type; @@ -4481,7 +5518,7 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection, { reply = g_dbus_message_new_method_error (message, "org.freedesktop.DBus.Error.UnknownMethod", - _("No such method `%s'"), + _("No such method '%s'"), g_dbus_message_get_member (message)); g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); @@ -4512,7 +5549,7 @@ validate_and_maybe_schedule_method_call (GDBusConnection *connection, reply = g_dbus_message_new_method_error (message, "org.freedesktop.DBus.Error.InvalidArgs", - _("Type of message, `%s', does not match expected type `%s'"), + _("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, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); @@ -4526,32 +5563,10 @@ 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); + schedule_method_call (connection, message, registration_id, subtree_registration_id, + interface_info, method_info, NULL, parameters, + vtable, main_context, user_data); g_variant_unref (parameters); - - /* TODO: would be nicer with a real MethodData like we already - * have PropertyData and PropertyGetAllData... */ - g_object_set_data (G_OBJECT (invocation), "g-dbus-interface-vtable", (gpointer) vtable); - g_object_set_data (G_OBJECT (invocation), "g-dbus-registration-id", GUINT_TO_POINTER (registration_id)); - g_object_set_data (G_OBJECT (invocation), "g-dbus-subtree-registration-id", GUINT_TO_POINTER (subtree_registration_id)); - - idle_source = g_idle_source_new (); - g_source_set_priority (idle_source, G_PRIORITY_DEFAULT); - g_source_set_callback (idle_source, - call_in_idle_cb, - invocation, - g_object_unref); - g_source_attach (idle_source, main_context); - g_source_unref (idle_source); - handled = TRUE; out: @@ -4560,7 +5575,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, @@ -4637,30 +5652,31 @@ obj_message_func (GDBusConnection *connection, /** * g_dbus_connection_register_object: - * @connection: A #GDBusConnection. - * @object_path: The object path to register at. - * @interface_info: Introspection data for the interface. - * @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. + * @connection: a #GDBusConnection + * @object_path: the object path to register at + * @interface_info: introspection data for the interface + * @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 * * Registers callbacks for exported objects at @object_path with the * D-Bus interface that is described in @interface_info. * - * Calls to functions in @vtable (and @user_data_free_func) will - * happen in the thread-default main - * loop of the thread you are calling this method from. + * Calls to functions in @vtable (and @user_data_free_func) will happen + * in the + * [thread-default main context][g-main-context-push-thread-default] + * of the thread you are calling this method from. * * Note that all #GVariant values passed to functions in @vtable will match * the signature given in @interface_info - if a remote caller passes - * incorrect values, the org.freedesktop.DBus.Error.InvalidArgs + * incorrect values, the `org.freedesktop.DBus.Error.InvalidArgs` * is returned to the remote caller. * * Additionally, if the remote caller attempts to invoke methods or * access properties not mentioned in @interface_info the - * org.freedesktop.DBus.Error.UnknownMethod resp. - * org.freedesktop.DBus.Error.InvalidArgs errors + * `org.freedesktop.DBus.Error.UnknownMethod` resp. + * `org.freedesktop.DBus.Error.InvalidArgs` errors * are returned to the caller. * * It is considered a programming error if the @@ -4672,31 +5688,30 @@ obj_message_func (GDBusConnection *connection, * * GDBus automatically implements the standard D-Bus interfaces * org.freedesktop.DBus.Properties, org.freedesktop.DBus.Introspectable - * and org.freedesktop.Peer, so you don't have to implement those for - * the objects you export. You can implement - * org.freedesktop.DBus.Properties yourself, e.g. to handle getting - * and setting of properties asynchronously. + * and org.freedesktop.Peer, so you don't have to implement those for the + * objects you export. You can implement org.freedesktop.DBus.Properties + * yourself, e.g. to handle getting and setting of properties asynchronously. * * Note that the reference count on @interface_info will be * incremented by 1 (unless allocated statically, e.g. if the * reference count is -1, see g_dbus_interface_info_ref()) for as long * as the object is exported. Also note that @vtable will be copied. * - * See for an example of how to use this method. + * See this [server][gdbus-server] for an example of how to use this method. * * Returns: 0 if @error is set, otherwise a registration id (never 0) - * that can be used with g_dbus_connection_unregister_object() . + * that can be used with g_dbus_connection_unregister_object() * * Since: 2.26 */ guint -g_dbus_connection_register_object (GDBusConnection *connection, - const gchar *object_path, - GDBusInterfaceInfo *interface_info, - const GDBusInterfaceVTable *vtable, - gpointer user_data, - GDestroyNotify user_data_free_func, - GError **error) +g_dbus_connection_register_object (GDBusConnection *connection, + const gchar *object_path, + GDBusInterfaceInfo *interface_info, + const GDBusInterfaceVTable *vtable, + gpointer user_data, + GDestroyNotify user_data_free_func, + GError **error) { ExportedObject *eo; ExportedInterface *ei; @@ -4707,6 +5722,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; @@ -4746,9 +5762,7 @@ g_dbus_connection_register_object (GDBusConnection *connection, 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, @@ -4767,12 +5781,13 @@ g_dbus_connection_register_object (GDBusConnection *connection, /** * g_dbus_connection_unregister_object: - * @connection: A #GDBusConnection. - * @registration_id: A registration id obtained from g_dbus_connection_register_object(). + * @connection: a #GDBusConnection + * @registration_id: a registration id obtained from + * g_dbus_connection_register_object() * * Unregisters an object. * - * Returns: %TRUE if the object was unregistered, %FALSE otherwise. + * Returns: %TRUE if the object was unregistered, %FALSE otherwise * * Since: 2.26 */ @@ -4785,6 +5800,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; @@ -4816,15 +5832,15 @@ g_dbus_connection_unregister_object (GDBusConnection *connection, /** * g_dbus_connection_emit_signal: - * @connection: A #GDBusConnection. - * @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: (allow-none): A #GVariant tuple with parameters for the signal - * or %NULL if not passing parameters. - * @error: Return location for error or %NULL. + * @connection: a #GDBusConnection + * @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: (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. * @@ -4832,7 +5848,7 @@ g_dbus_connection_unregister_object (GDBusConnection *connection, * * This can only fail if @parameters is not compatible with the D-Bus protocol. * - * Returns: %TRUE unless @error is set. + * Returns: %TRUE unless @error is set * * Since: 2.26 */ @@ -4857,6 +5873,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 ())) { @@ -4929,7 +5946,7 @@ decode_method_reply (GDBusMessage *reply, g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("Method `%s' returned type `%s', but expected `%s'"), + _("Method '%s' returned type '%s', but expected '%s'"), method_name, g_variant_get_type_string (result), type_string); g_variant_unref (result); @@ -4987,6 +6004,7 @@ call_state_free (CallState *state) 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, @@ -5038,11 +6056,12 @@ g_dbus_connection_call_done (GObject *source, { g_simple_async_result_set_op_res_gpointer (state->simple, state, (GDestroyNotify) call_state_free); g_simple_async_result_complete (state->simple); - g_object_unref (reply); } + 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, @@ -5059,7 +6078,7 @@ g_dbus_connection_call_internal (GDBusConnection *connection, gpointer user_data) { GDBusMessage *message; - CallState *state; + guint32 serial; g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); g_return_if_fail (bus_name == NULL || g_dbus_is_name (bus_name)); @@ -5068,23 +6087,13 @@ g_dbus_connection_call_internal (GDBusConnection *connection, 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); - 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, @@ -5098,14 +6107,50 @@ g_dbus_connection_call_internal (GDBusConnection *connection, 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 the user has no callback then we can just send the message with + * the G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED flag set and skip all + * the logic for processing the reply. If the service sends the reply + * anyway then it will just be ignored. + */ + if (callback != NULL) + { + CallState *state; + + 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 (reply_type == NULL) + reply_type = G_VARIANT_TYPE_ANY; + + state->reply_type = g_variant_type_copy (reply_type); + + 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); + serial = state->serial; + } + else + { + GDBusMessageFlags flags; + + flags = g_dbus_message_get_flags (message); + flags |= G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED; + g_dbus_message_set_flags (message, flags); + + g_dbus_connection_send_message (connection, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + &serial, NULL); + } if (G_UNLIKELY (_g_dbus_debug_call ())) { @@ -5119,7 +6164,7 @@ g_dbus_connection_call_internal (GDBusConnection *connection, method_name, object_path, bus_name != NULL ? bus_name : "(none)", - state->serial); + serial); _g_dbus_debug_print_unlock (); } @@ -5127,6 +6172,7 @@ g_dbus_connection_call_internal (GDBusConnection *connection, g_object_unref (message); } +/* 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, @@ -5152,6 +6198,7 @@ g_dbus_connection_call_finish_internal (GDBusConnection *connection, 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, @@ -5171,6 +6218,7 @@ g_dbus_connection_call_sync_internal (GDBusConnection *connection, GDBusMessage *reply; GVariant *result; GError *local_error; + GDBusSendMessageFlags send_flags; message = NULL; reply = NULL; @@ -5190,6 +6238,9 @@ g_dbus_connection_call_sync_internal (GDBusConnection *connection, #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; @@ -5222,9 +6273,16 @@ g_dbus_connection_call_sync_internal (GDBusConnection *connection, } 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, - G_DBUS_SEND_MESSAGE_FLAGS_NONE, + send_flags, timeout_msec, NULL, /* volatile guint32 *out_serial */ cancellable, @@ -5275,23 +6333,23 @@ g_dbus_connection_call_sync_internal (GDBusConnection *connection, /** * g_dbus_connection_call: - * @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. - * @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. + * @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 + * @cancellable: (allow-none): 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 * * Asynchronously invokes the @method_name method on the * @interface_name D-Bus interface on the remote object at @@ -5309,7 +6367,7 @@ g_dbus_connection_call_sync_internal (GDBusConnection *connection, * * If the @parameters #GVariant is floating, it is consumed. This allows * convenient 'inline' use of g_variant_new(), e.g.: - * |[ + * |[ * g_dbus_connection_call (connection, * "org.freedesktop.StringThings", * "/org/freedesktop/StringThings", @@ -5326,42 +6384,46 @@ g_dbus_connection_call_sync_internal (GDBusConnection *connection, * NULL); * ]| * - * This is an asynchronous method. When the operation is finished, @callback will be invoked - * in the thread-default main loop + * This is an asynchronous method. When the operation is finished, + * @callback will be invoked in the + * [thread-default main context][g-main-context-push-thread-default] * of the thread you are calling this method from. You can then call * g_dbus_connection_call_finish() to get the result of the operation. * See g_dbus_connection_call_sync() for the synchronous version of this * function. * + * If @callback is %NULL then the D-Bus method call message will be sent with + * the %G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED flag set. + * * Since: 2.26 */ void -g_dbus_connection_call (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, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +g_dbus_connection_call (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, + 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, NULL, cancellable, callback, user_data); } /** * g_dbus_connection_call_finish: - * @connection: A #GDBusConnection. - * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_call(). - * @error: Return location for error or %NULL. + * @connection: a #GDBusConnection + * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_call() + * @error: return location for error or %NULL * * Finishes an operation started with g_dbus_connection_call(). * * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with - * return values. Free with g_variant_unref(). + * return values. Free with g_variant_unref(). * * Since: 2.26 */ @@ -5375,19 +6437,20 @@ g_dbus_connection_call_finish (GDBusConnection *connection, /** * g_dbus_connection_call_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. - * @cancellable: A #GCancellable or %NULL. - * @error: Return location for error or %NULL. + * @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 + * @cancellable: (allow-none): a #GCancellable or %NULL + * @error: return location for error or %NULL * * Synchronously invokes the @method_name method on the * @interface_name D-Bus interface on the remote object at @@ -5406,7 +6469,7 @@ g_dbus_connection_call_finish (GDBusConnection *connection, * * If the @parameters #GVariant is floating, it is consumed. * This allows convenient 'inline' use of g_variant_new(), e.g.: - * |[ + * |[ * g_dbus_connection_call_sync (connection, * "org.freedesktop.StringThings", * "/org/freedesktop/StringThings", @@ -5419,7 +6482,7 @@ g_dbus_connection_call_finish (GDBusConnection *connection, * G_DBUS_CALL_FLAGS_NONE, * -1, * NULL, - * &error); + * &error); * ]| * * The calling thread is blocked until a reply is received. See @@ -5427,22 +6490,22 @@ g_dbus_connection_call_finish (GDBusConnection *connection, * this method. * * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with - * return values. Free with g_variant_unref(). + * return values. Free with g_variant_unref(). * * Since: 2.26 */ GVariant * -g_dbus_connection_call_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, - GCancellable *cancellable, - GError **error) +g_dbus_connection_call_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, + 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, NULL, NULL, cancellable, error); } @@ -5453,23 +6516,23 @@ g_dbus_connection_call_sync (GDBusConnection *connection, /** * 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. + * @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: (allow-none): 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. @@ -5479,34 +6542,35 @@ g_dbus_connection_call_sync (GDBusConnection *connection, * 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_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); } /** * 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. + * @connection: a #GDBusConnection + * @out_fd_list: (out) (allow-none): 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(). + * return values. Free with g_variant_unref(). * * Since: 2.30 */ @@ -5521,45 +6585,46 @@ g_dbus_connection_call_with_unix_fd_list_finish (GDBusConnection *connection, /** * 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. + * @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 + * @out_fd_list: (out) (allow-none): return location for a #GUnixFDList or %NULL + * @cancellable: (allow-none): 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(). + * 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) +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); } @@ -5588,15 +6653,16 @@ exported_subtree_free (ExportedSubtree *es) 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, @@ -5705,7 +6771,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, @@ -5850,7 +6918,7 @@ handle_subtree_method_invocation (GDBusConnection *connection, GDBusMessage *reply; reply = g_dbus_message_new_method_error (message, "org.freedesktop.DBus.Error.InvalidArgs", - _("No such interface `%s'"), + _("No such interface '%s'"), interface_name); g_dbus_connection_send_message (es->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); g_object_unref (reply); @@ -5961,7 +7029,7 @@ process_subtree_vtable_message_in_idle_cb (gpointer _data) GDBusMessage *reply; reply = g_dbus_message_new_method_error (data->message, "org.freedesktop.DBus.Error.UnknownMethod", - _("Method `%s' on interface `%s' with signature `%s' does not exist"), + _("Method '%s' on interface '%s' with signature '%s' does not exist"), g_dbus_message_get_member (data->message), g_dbus_message_get_interface (data->message), g_dbus_message_get_signature (data->message)); @@ -5972,7 +7040,7 @@ process_subtree_vtable_message_in_idle_cb (gpointer _data) 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, @@ -5992,6 +7060,7 @@ subtree_message_func (GDBusConnection *connection, process_subtree_vtable_message_in_idle_cb, data, (GDestroyNotify) subtree_deferred_data_free); + g_source_set_name (idle_source, "[gio] process_subtree_vtable_message_in_idle_cb"); g_source_attach (idle_source, es->context); g_source_unref (idle_source); @@ -6004,15 +7073,16 @@ subtree_message_func (GDBusConnection *connection, /** * g_dbus_connection_register_subtree: - * @connection: A #GDBusConnection. - * @object_path: The object path to register the subtree at. - * @vtable: A #GDBusSubtreeVTable to enumerate, introspect and dispatch nodes in the subtree. - * @flags: Flags used to fine tune the behavior of the subtree. - * @user_data: Data to pass to functions in @vtable. - * @user_data_free_func: Function to call when the subtree is unregistered. - * @error: Return location for error or %NULL. + * @connection: a #GDBusConnection + * @object_path: the object path to register the subtree at + * @vtable: a #GDBusSubtreeVTable to enumerate, introspect and + * dispatch nodes in the subtree + * @flags: flags used to fine tune the behavior of the subtree + * @user_data: data to pass to functions in @vtable + * @user_data_free_func: function to call when the subtree is unregistered + * @error: return location for error or %NULL * - * Registers a whole subtree of dynamic objects. + * Registers a whole subtree of dynamic objects. * * The @enumerate and @introspection functions in @vtable are used to * convey, to remote callers, what nodes exist in the subtree rooted @@ -6027,9 +7097,9 @@ subtree_message_func (GDBusConnection *connection, * #gpointer will be used to call into the interface vtable for processing * the request. * - * All calls into user-provided code will be invoked in the thread-default main - * loop of the thread you are calling this method from. + * All calls into user-provided code will be invoked in the + * [thread-default main context][g-main-context-push-thread-default] + * of the thread you are calling this method from. * * If an existing subtree is already registered at @object_path or * then @error is set to #G_IO_ERROR_EXISTS. @@ -6038,14 +7108,14 @@ subtree_message_func (GDBusConnection *connection, * g_dbus_connection_register_object()) in a subtree registered with * g_dbus_connection_register_subtree() - if so, the subtree handler * is tried as the last resort. One way to think about a subtree - * handler is to consider it a fallback handler - * for object paths not registered via g_dbus_connection_register_object() - * or other bindings. + * handler is to consider it a fallback handler for object paths not + * registered via g_dbus_connection_register_object() or other bindings. * * Note that @vtable will be copied so you cannot change it after * registration. * - * See for an example of how to use this method. + * See this [server][gdbus-subtree-server] for an example of how to use + * this method. * * Returns: 0 if @error is set, otherwise a subtree registration id (never 0) * that can be used with g_dbus_connection_unregister_subtree() . @@ -6068,6 +7138,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; @@ -6093,9 +7164,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, @@ -6114,12 +7183,13 @@ g_dbus_connection_register_subtree (GDBusConnection *connection, /** * g_dbus_connection_unregister_subtree: - * @connection: A #GDBusConnection. - * @registration_id: A subtree registration id obtained from g_dbus_connection_register_subtree(). + * @connection: a #GDBusConnection + * @registration_id: a subtree registration id obtained from + * g_dbus_connection_register_subtree() * * Unregisters a subtree. * - * Returns: %TRUE if the subtree was unregistered, %FALSE otherwise. + * Returns: %TRUE if the subtree was unregistered, %FALSE otherwise * * Since: 2.26 */ @@ -6131,6 +7201,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; @@ -6154,7 +7225,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, @@ -6166,7 +7237,7 @@ handle_generic_ping_unlocked (GDBusConnection *connection, 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, @@ -6199,7 +7270,7 @@ handle_generic_get_machine_id_unlocked (GDBusConnection *connection, 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, @@ -6227,7 +7298,7 @@ handle_generic_introspect_unlocked (GDBusConnection *connection, 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) @@ -6274,7 +7345,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) @@ -6356,7 +7427,7 @@ distribute_method_call (GDBusConnection *connection, /* if we end up here, the message has not been not handled - so return an error saying this */ reply = g_dbus_message_new_method_error (message, "org.freedesktop.DBus.Error.UnknownMethod", - _("No such interface `%s' on object at path %s"), + _("No such interface '%s' on object at path %s"), interface_name, object_path); g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL); @@ -6368,11 +7439,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; @@ -6387,6 +7459,14 @@ message_bus_get_singleton (GBusType bus_type, ret = &the_system_bus; break; + case G_BUS_TYPE_USER: + ret = &the_user_bus; + break; + + case G_BUS_TYPE_MACHINE: + ret = &the_machine_bus; + break; + case G_BUS_TYPE_STARTER: starter_bus = g_getenv ("DBUS_STARTER_BUS_TYPE"); if (g_strcmp0 (starter_bus, "session") == 0) @@ -6407,7 +7487,7 @@ message_bus_get_singleton (GBusType bus_type, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, _("Cannot determine bus address from DBUS_STARTER_BUS_TYPE environment variable" - " - unknown value `%s'"), + " - unknown value '%s'"), starter_bus); } else @@ -6430,12 +7510,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; @@ -6445,24 +7526,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); @@ -6471,11 +7552,30 @@ get_uninitialized_connection (GBusType bus_type, return ret; } +/* May be called from any thread. Must not hold message_bus_lock. */ +GDBusConnection * +_g_bus_get_singleton_if_exists (GBusType bus_type) +{ + GWeakRef *singleton; + GDBusConnection *ret = NULL; + + G_LOCK (message_bus_lock); + singleton = message_bus_get_singleton (bus_type, NULL); + if (singleton == NULL) + goto out; + + ret = g_weak_ref_get (singleton); + + out: + G_UNLOCK (message_bus_lock); + return ret; +} + /** * g_bus_get_sync: - * @bus_type: A #GBusType. - * @cancellable: A #GCancellable or %NULL. - * @error: Return location for error or %NULL. + * @bus_type: a #GBusType + * @cancellable: (allow-none): a #GCancellable or %NULL + * @error: return location for error or %NULL * * Synchronously connects to the message bus specified by @bus_type. * Note that the returned object may shared with other callers, @@ -6494,7 +7594,8 @@ 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: (transfer full): 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 */ @@ -6550,10 +7651,10 @@ bus_get_async_initable_cb (GObject *source_object, /** * g_bus_get: - * @bus_type: A #GBusType. - * @cancellable: A #GCancellable or %NULL. - * @callback: A #GAsyncReadyCallback to call when the request is satisfied. - * @user_data: The data to pass to @callback. + * @bus_type: a #GBusType + * @cancellable: (allow-none): a #GCancellable or %NULL + * @callback: a #GAsyncReadyCallback to call when the request is satisfied + * @user_data: the data to pass to @callback * * Asynchronously connects to the message bus specified by @bus_type. * @@ -6579,6 +7680,7 @@ 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); @@ -6601,8 +7703,9 @@ g_bus_get (GBusType bus_type, /** * g_bus_get_finish: - * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_bus_get(). - * @error: Return location for error or %NULL. + * @res: a #GAsyncResult obtained from the #GAsyncReadyCallback passed + * to g_bus_get() + * @error: return location for error or %NULL * * Finishes an operation started with g_bus_get(). * @@ -6615,7 +7718,8 @@ g_bus_get (GBusType bus_type, * Note that the returned #GDBusConnection object will (usually) have * the #GDBusConnection:exit-on-close property set to %TRUE. * - * Returns: (transfer full): 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 */