Bug 632544 – Cannot send a locked message with PRESERVE_SERIAL flag
[platform/upstream/glib.git] / gio / gdbusconnection.c
index 62b9261..809f976 100644 (file)
 /*
  * TODO for GDBus:
  *
- * - Validate all data (e.g. UTF-8) and check all the required D-Bus headers
- *   are present and forbidden ones aren't
- *   - When writing: g_dbus_message_to_blob()
- *   - When reading: g_dbus_message_new_from_blob()
- *
  * - would be nice to expose GDBusAuthMechanism and an extension point
  *
  * - Need to rewrite GDBusAuth and rework GDBusAuthMechanism. In particular
  *      kind of pitfalls it avoids
  *      - Export objects before claiming names
  *    - Talk about auto-starting services (cf. GBusNameWatcherFlags)
+ *
+ *  - use abstract sockets in test code
+ *   - right now it doesn't work, dbus-daemon(1) fails with
+ *
+ *        /gdbus/connection/filter: Failed to start message bus: Failed to bind
+ *        socket "/tmp/g-dbus-tests-pid-28531": Address already in use
+ *        ** WARNING **: Error reading address from dbus daemon, 0 bytes read
+ *
+ *     or similar.
  */
 
 #include "config.h"
@@ -185,6 +189,77 @@ static GDBusConnection *the_system_bus = NULL;
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+typedef struct
+{
+  GDestroyNotify              callback;
+  gpointer                    user_data;
+  GMainContext               *context;
+} CallDestroyNotifyData;
+
+static gboolean
+call_destroy_notify_data_in_idle (gpointer user_data)
+{
+  CallDestroyNotifyData *data = user_data;
+  data->callback (data->user_data);
+  return FALSE;
+}
+
+static void
+call_destroy_notify_data_free (CallDestroyNotifyData *data)
+{
+  if (data->context != NULL)
+    g_main_context_unref (data->context);
+  g_free (data);
+}
+
+/*
+ * call_destroy_notify: <internal>
+ * @context: A #GMainContext or %NULL.
+ * @callback: A #GDestroyNotify or %NULL.
+ * @user_data: Data to pass to @callback.
+ *
+ * Schedules @callback to run in @context.
+ */
+static void
+call_destroy_notify (GMainContext  *context,
+                     GDestroyNotify callback,
+                     gpointer       user_data)
+{
+  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);
+
+      idle_source = g_idle_source_new ();
+      g_source_set_priority (idle_source, G_PRIORITY_DEFAULT);
+      g_source_set_callback (idle_source,
+                             call_destroy_notify_data_in_idle,
+                             data,
+                             (GDestroyNotify) call_destroy_notify_data_free);
+      g_source_attach (idle_source, data->context);
+      g_source_unref (idle_source);
+    }
+
+ out:
+  ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static gboolean
 _g_strv_has_string (const gchar* const *haystack,
                     const gchar        *needle)
@@ -317,7 +392,10 @@ struct _GDBusConnection
   GDBusCapabilityFlags capabilities;
 
   GDBusAuthObserver *authentication_observer;
-  GCredentials *crendentials;
+  GCredentials *credentials;
+
+  /* set to TRUE when finalizing */
+  gboolean finalizing;
 };
 
 typedef struct ExportedObject ExportedObject;
@@ -377,13 +455,14 @@ G_DEFINE_TYPE_WITH_CODE (GDBusConnection, g_dbus_connection, G_TYPE_OBJECT,
                          G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
                          );
 
+static GHashTable *alive_connections = NULL;
+
 static void
 g_dbus_connection_dispose (GObject *object)
 {
   GDBusConnection *connection = G_DBUS_CONNECTION (object);
 
   G_LOCK (message_bus_lock);
-  //g_debug ("disposing %p", connection);
   if (connection == the_session_bus)
     {
       the_session_bus = NULL;
@@ -392,11 +471,20 @@ g_dbus_connection_dispose (GObject *object)
     {
       the_system_bus = NULL;
     }
+  CONNECTION_LOCK (connection);
   if (connection->worker != NULL)
     {
       _g_dbus_worker_stop (connection->worker);
       connection->worker = NULL;
+      if (alive_connections != NULL)
+        g_warn_if_fail (g_hash_table_remove (alive_connections, connection));
     }
+  else
+    {
+      if (alive_connections != NULL)
+        g_warn_if_fail (g_hash_table_lookup (alive_connections, connection) == NULL);
+    }
+  CONNECTION_UNLOCK (connection);
   G_UNLOCK (message_bus_lock);
 
   if (G_OBJECT_CLASS (g_dbus_connection_parent_class)->dispose != NULL)
@@ -408,13 +496,22 @@ g_dbus_connection_finalize (GObject *object)
 {
   GDBusConnection *connection = G_DBUS_CONNECTION (object);
 
+  connection->finalizing = TRUE;
+
+  purge_all_signal_subscriptions (connection);
+
+  purge_all_filters (connection);
+  g_ptr_array_unref (connection->filters);
+
   if (connection->authentication_observer != NULL)
     g_object_unref (connection->authentication_observer);
 
   if (connection->auth != NULL)
     g_object_unref (connection->auth);
 
-  //g_debug ("finalizing %p", connection);
+  if (connection->credentials)
+    g_object_unref (connection->credentials);
+
   if (connection->stream != NULL)
     {
       /* We don't really care if closing the stream succeeds or not */
@@ -437,7 +534,6 @@ g_dbus_connection_finalize (GObject *object)
 
   g_hash_table_unref (connection->map_method_serial_to_send_message_data);
 
-  purge_all_signal_subscriptions (connection);
   g_hash_table_unref (connection->map_rule_to_signal_data);
   g_hash_table_unref (connection->map_id_to_signal_data);
   g_hash_table_unref (connection->map_sender_unique_name_to_signal_data_array);
@@ -447,9 +543,6 @@ g_dbus_connection_finalize (GObject *object)
   g_hash_table_unref (connection->map_id_to_es);
   g_hash_table_unref (connection->map_object_path_to_es);
 
-  purge_all_filters (connection);
-  g_ptr_array_unref (connection->filters);
-
   if (connection->main_context_at_construction != NULL)
     g_main_context_unref (connection->main_context_at_construction);
 
@@ -548,7 +641,17 @@ g_dbus_connection_real_closed (GDBusConnection *connection,
 {
   if (remote_peer_vanished && connection->exit_on_close)
     {
-      g_print ("%s: Remote peer vanished. Exiting.\n", G_STRFUNC);
+      if (error != NULL)
+        {
+          g_print ("%s: Remote peer vanished with error: %s (%s, %d). Exiting.\n",
+                   G_STRFUNC,
+                   error->message,
+                   g_quark_to_string (error->domain), error->code);
+        }
+      else
+        {
+          g_print ("%s: Remote peer vanished. Exiting.\n", G_STRFUNC);
+        }
       raise (SIGTERM);
     }
 }
@@ -816,7 +919,7 @@ g_dbus_connection_init (GDBusConnection *connection)
   connection->map_sender_unique_name_to_signal_data_array = g_hash_table_new_full (g_str_hash,
                                                                                    g_str_equal,
                                                                                    g_free,
-                                                                                   NULL);
+                                                                                   (GDestroyNotify) g_ptr_array_unref);
 
   connection->map_object_path_to_eo = g_hash_table_new_full (g_str_hash,
                                                              g_str_equal,
@@ -847,7 +950,7 @@ g_dbus_connection_init (GDBusConnection *connection)
  *
  * Gets the underlying stream used for IO.
  *
- * Returns: the stream used for IO
+ * Returns: (transfer none): the stream used for IO
  *
  * Since: 2.26
  */
@@ -923,10 +1026,7 @@ flush_in_thread_func (GSimpleAsyncResult *res,
   if (!g_dbus_connection_flush_sync (G_DBUS_CONNECTION (object),
                                      cancellable,
                                      &error))
-    {
-      g_simple_async_result_set_from_error (res, error);
-      g_error_free (error);
-    }
+    g_simple_async_result_take_error (res, error);
 }
 
 /**
@@ -965,7 +1065,7 @@ g_dbus_connection_flush (GDBusConnection     *connection,
 
   g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
 
-  simple = g_simple_async_result_new (NULL,
+  simple = g_simple_async_result_new (G_OBJECT (connection),
                                       callback,
                                       user_data,
                                       g_dbus_connection_flush);
@@ -1134,10 +1234,7 @@ close_in_thread_func (GSimpleAsyncResult *res,
   if (!g_dbus_connection_close_sync (G_DBUS_CONNECTION (object),
                                      cancellable,
                                      &error))
-    {
-      g_simple_async_result_set_from_error (res, error);
-      g_error_free (error);
-    }
+    g_simple_async_result_take_error (res, error);
 }
 
 /**
@@ -1185,7 +1282,7 @@ g_dbus_connection_close (GDBusConnection     *connection,
 
   g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
 
-  simple = g_simple_async_result_new (NULL,
+  simple = g_simple_async_result_new (G_OBJECT (connection),
                                       callback,
                                       user_data,
                                       g_dbus_connection_close);
@@ -1286,6 +1383,7 @@ g_dbus_connection_close_sync (GDBusConnection     *connection,
 static gboolean
 g_dbus_connection_send_message_unlocked (GDBusConnection   *connection,
                                          GDBusMessage      *message,
+                                         GDBusSendMessageFlags flags,
                                          volatile guint32  *out_serial,
                                          GError           **error)
 {
@@ -1323,11 +1421,10 @@ g_dbus_connection_send_message_unlocked (GDBusConnection   *connection,
   if (blob == NULL)
     goto out;
 
-  serial_to_use = g_dbus_message_get_serial (message);
-  if (serial_to_use == 0)
-    {
-      serial_to_use = ++connection->last_serial; /* TODO: handle overflow */
-    }
+  if (flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL)
+    serial_to_use = g_dbus_message_get_serial (message);
+  else
+    serial_to_use = ++connection->last_serial; /* TODO: handle overflow */
 
   switch (blob[0])
     {
@@ -1355,8 +1452,10 @@ g_dbus_connection_send_message_unlocked (GDBusConnection   *connection,
   if (out_serial != NULL)
     *out_serial = serial_to_use;
 
-  g_dbus_message_set_serial (message, serial_to_use);
+  if (!(flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL))
+    g_dbus_message_set_serial (message, serial_to_use);
 
+  g_dbus_message_lock (message);
   _g_dbus_worker_send_message (connection->worker,
                                message,
                                (gchar*) blob,
@@ -1375,16 +1474,17 @@ 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: Return location for serial number assigned to @message when sending it or %NULL.
  * @error: Return location for error or %NULL.
  *
  * Asynchronously sends @message to the peer represented by @connection.
  *
- * If g_dbus_message_get_serial() returns non-zero for @message, then
- * that value is used for the message serial number. Otherwise a
- * serial number will be assigned by @connection and set on @message
- * via g_dbus_message_set_serial(). If @out_serial is not %NULL, then
- * the serial number used will be written to this location prior to
+ * Unless @flags contain the
+ * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag, the serial number
+ * will be assigned by @connection and set on @message via
+ * g_dbus_message_set_serial(). If @out_serial is not %NULL, then the
+ * serial number used will be written to this location prior to
  * submitting the message to the underlying transport.
  *
  * If @connection is closed then the operation will fail with
@@ -1395,6 +1495,9 @@ g_dbus_connection_send_message_unlocked (GDBusConnection   *connection,
  * linkend="gdbus-unix-fd-client"/> for an example of how to use this
  * low-level API to send and receive UNIX file descriptors.
  *
+ * Note that @message must be unlocked, unless @flags contain the
+ * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag.
+ *
  * Returns: %TRUE if the message was well-formed and queued for
  * transmission, %FALSE if @error is set.
  *
@@ -1403,6 +1506,7 @@ g_dbus_connection_send_message_unlocked (GDBusConnection   *connection,
 gboolean
 g_dbus_connection_send_message (GDBusConnection   *connection,
                                 GDBusMessage      *message,
+                                GDBusSendMessageFlags flags,
                                 volatile guint32  *out_serial,
                                 GError           **error)
 {
@@ -1410,10 +1514,11 @@ g_dbus_connection_send_message (GDBusConnection   *connection,
 
   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
   g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), FALSE);
+  g_return_val_if_fail ((flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) || !g_dbus_message_get_locked (message), FALSE);
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
   CONNECTION_LOCK (connection);
-  ret = g_dbus_connection_send_message_unlocked (connection, message, out_serial, error);
+  ret = g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, error);
   CONNECTION_UNLOCK (connection);
   return ret;
 }
@@ -1588,6 +1693,7 @@ send_message_with_reply_timeout_cb (gpointer user_data)
 static void
 g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection     *connection,
                                                     GDBusMessage        *message,
+                                                    GDBusSendMessageFlags flags,
                                                     gint                 timeout_msec,
                                                     volatile guint32    *out_serial,
                                                     GCancellable        *cancellable,
@@ -1635,9 +1741,9 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection     *connect
     }
 
   error = NULL;
-  if (!g_dbus_connection_send_message_unlocked (connection, message, out_serial, &error))
+  if (!g_dbus_connection_send_message_unlocked (connection, message, flags, out_serial, &error))
     {
-      g_simple_async_result_set_from_error (simple, error);
+      g_simple_async_result_take_error (simple, error);
       g_simple_async_result_complete_in_idle (simple);
       g_object_unref (simple);
       goto out;
@@ -1665,14 +1771,17 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection     *connect
                               (GDestroyNotify) g_object_unref);
     }
 
-  data->timeout_source = g_timeout_source_new (timeout_msec);
-  g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT);
-  g_source_set_callback (data->timeout_source,
-                         send_message_with_reply_timeout_cb,
-                         send_message_data_ref (data),
-                         (GDestroyNotify) send_message_data_unref);
-  g_source_attach (data->timeout_source, data->main_context);
-  g_source_unref (data->timeout_source);
+  if (timeout_msec != G_MAXINT)
+    {
+      data->timeout_source = g_timeout_source_new (timeout_msec);
+      g_source_set_priority (data->timeout_source, G_PRIORITY_DEFAULT);
+      g_source_set_callback (data->timeout_source,
+                             send_message_with_reply_timeout_cb,
+                             send_message_data_ref (data),
+                             (GDestroyNotify) send_message_data_unref);
+      g_source_attach (data->timeout_source, data->main_context);
+      g_source_unref (data->timeout_source);
+    }
 
   g_hash_table_insert (connection->map_method_serial_to_send_message_data,
                        GUINT_TO_POINTER (*out_serial),
@@ -1686,7 +1795,9 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection     *connect
  * g_dbus_connection_send_message_with_reply:
  * @connection: A #GDBusConnection.
  * @message: A #GDBusMessage.
- * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout.
+ * @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: Return location for serial number assigned to @message when sending it or %NULL.
  * @cancellable: A #GCancellable or %NULL.
  * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't
@@ -1695,11 +1806,11 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection     *connect
  *
  * Asynchronously sends @message to the peer represented by @connection.
  *
- * If g_dbus_message_get_serial() returns non-zero for @message, then
- * that value is used for the message serial number. Otherwise a
- * serial number will be assigned by @connection and set on @message
- * via g_dbus_message_set_serial(). If @out_serial is not %NULL, then
- * the serial number used will be written to this location prior to
+ * Unless @flags contain the
+ * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag, the serial number
+ * will be assigned by @connection and set on @message via
+ * g_dbus_message_set_serial(). If @out_serial is not %NULL, then the
+ * serial number used will be written to this location prior to
  * submitting the message to the underlying transport.
  *
  * If @connection is closed then the operation will fail with
@@ -1713,6 +1824,9 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection     *connect
  * g_dbus_connection_send_message_with_reply_finish() to get the result of the operation.
  * See g_dbus_connection_send_message_with_reply_sync() for the synchronous version.
  *
+ * Note that @message must be unlocked, unless @flags contain the
+ * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag.
+ *
  * See <xref linkend="gdbus-server"/> and <xref
  * linkend="gdbus-unix-fd-client"/> for an example of how to use this
  * low-level API to send and receive UNIX file descriptors.
@@ -1722,6 +1836,7 @@ g_dbus_connection_send_message_with_reply_unlocked (GDBusConnection     *connect
 void
 g_dbus_connection_send_message_with_reply (GDBusConnection     *connection,
                                            GDBusMessage        *message,
+                                           GDBusSendMessageFlags flags,
                                            gint                 timeout_msec,
                                            volatile guint32    *out_serial,
                                            GCancellable        *cancellable,
@@ -1730,11 +1845,13 @@ g_dbus_connection_send_message_with_reply (GDBusConnection     *connection,
 {
   g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
   g_return_if_fail (G_IS_DBUS_MESSAGE (message));
+  g_return_if_fail ((flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) || !g_dbus_message_get_locked (message));
   g_return_if_fail (timeout_msec >= 0 || timeout_msec == -1);
 
   CONNECTION_LOCK (connection);
   g_dbus_connection_send_message_with_reply_unlocked (connection,
                                                       message,
+                                                      flags,
                                                       timeout_msec,
                                                       out_serial,
                                                       cancellable,
@@ -1760,7 +1877,7 @@ g_dbus_connection_send_message_with_reply (GDBusConnection     *connection,
  * linkend="gdbus-unix-fd-client"/> for an example of how to use this
  * low-level API to send and receive UNIX file descriptors.
  *
- * Returns: A #GDBusMessage or %NULL if @error is set.
+ * Returns: (transfer full): A locked #GDBusMessage or %NULL if @error is set.
  *
  * Since: 2.26
  */
@@ -1821,7 +1938,9 @@ send_message_with_reply_sync_cb (GDBusConnection *connection,
  * g_dbus_connection_send_message_with_reply_sync:
  * @connection: A #GDBusConnection.
  * @message: A #GDBusMessage.
- * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout.
+ * @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: Return location for serial number assigned to @message when sending it or %NULL.
  * @cancellable: A #GCancellable or %NULL.
  * @error: Return location for error or %NULL.
@@ -1831,11 +1950,11 @@ send_message_with_reply_sync_cb (GDBusConnection *connection,
  * timeout is reached. See g_dbus_connection_send_message_with_reply()
  * for the asynchronous version of this method.
  *
- * If g_dbus_message_get_serial() returns non-zero for @message, then
- * that value is used for the message serial number. Otherwise a
- * serial number will be assigned by @connection and set on @message
- * via g_dbus_message_set_serial(). If @out_serial is not %NULL, then
- * the serial number used will be written to this location prior to
+ * Unless @flags contain the
+ * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag, the serial number
+ * will be assigned by @connection and set on @message via
+ * g_dbus_message_set_serial(). If @out_serial is not %NULL, then the
+ * serial number used will be written to this location prior to
  * submitting the message to the underlying transport.
  *
  * If @connection is closed then the operation will fail with
@@ -1852,13 +1971,17 @@ send_message_with_reply_sync_cb (GDBusConnection *connection,
  * linkend="gdbus-unix-fd-client"/> for an example of how to use this
  * low-level API to send and receive UNIX file descriptors.
  *
- * Returns: A #GDBusMessage that is the reply to @message or %NULL if @error is set.
+ * Note that @message must be unlocked, unless @flags contain the
+ * %G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL flag.
+ *
+ * Returns: (transfer full): A locked #GDBusMessage that is the reply to @message or %NULL if @error is set.
  *
  * Since: 2.26
  */
 GDBusMessage *
 g_dbus_connection_send_message_with_reply_sync (GDBusConnection   *connection,
                                                 GDBusMessage      *message,
+                                                GDBusSendMessageFlags flags,
                                                 gint               timeout_msec,
                                                 volatile guint32  *out_serial,
                                                 GCancellable      *cancellable,
@@ -1869,6 +1992,7 @@ g_dbus_connection_send_message_with_reply_sync (GDBusConnection   *connection,
 
   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
   g_return_val_if_fail (G_IS_DBUS_MESSAGE (message), NULL);
+  g_return_val_if_fail ((flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL) || !g_dbus_message_get_locked (message), FALSE);
   g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
@@ -1880,6 +2004,7 @@ g_dbus_connection_send_message_with_reply_sync (GDBusConnection   *connection,
 
   g_dbus_connection_send_message_with_reply (connection,
                                              message,
+                                             flags,
                                              timeout_msec,
                                              out_serial,
                                              cancellable,
@@ -1922,15 +2047,31 @@ on_worker_message_received (GDBusWorker  *worker,
                             GDBusMessage *message,
                             gpointer      user_data)
 {
-  GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
+  GDBusConnection *connection;
   FilterCallback *filters;
   gboolean consumed_by_filter;
+  gboolean altered_by_filter;
   guint num_filters;
   guint n;
+  gboolean alive;
+
+  G_LOCK (message_bus_lock);
+  alive = (g_hash_table_lookup (alive_connections, user_data) != NULL);
+  if (!alive)
+    {
+      G_UNLOCK (message_bus_lock);
+      return;
+    }
+  connection = G_DBUS_CONNECTION (user_data);
+  g_object_ref (connection);
+  G_UNLOCK (message_bus_lock);
 
   //g_debug ("in on_worker_message_received");
 
-  g_object_ref (connection);
+  g_object_ref (message);
+  g_dbus_message_lock (message);
+
+  //g_debug ("boo ref_count = %d %p %p", G_OBJECT (connection)->ref_count, connection, connection->worker);
 
   /* First collect the set of callback functions */
   CONNECTION_LOCK (connection);
@@ -1944,20 +2085,24 @@ on_worker_message_received (GDBusWorker  *worker,
     }
   CONNECTION_UNLOCK (connection);
 
-  /* the call the filters in order (without holding the lock) */
+  /* then call the filters in order (without holding the lock) */
   consumed_by_filter = FALSE;
+  altered_by_filter = FALSE;
   for (n = 0; n < num_filters; n++)
     {
-      consumed_by_filter = filters[n].func (connection,
-                                            message,
-                                            TRUE,
-                                            filters[n].user_data);
-      if (consumed_by_filter)
+      message = filters[n].func (connection,
+                                 message,
+                                 TRUE,
+                                 filters[n].user_data);
+      if (message == NULL)
         break;
+      g_dbus_message_lock (message);
     }
 
-  /* Standard dispatch unless the filter ate the message */
-  if (!consumed_by_filter)
+  /* Standard dispatch unless the filter ate the message - no need to
+   * do anything if the message was altered
+   */
+  if (message != NULL)
     {
       GDBusMessageType message_type;
 
@@ -1996,25 +2141,36 @@ on_worker_message_received (GDBusWorker  *worker,
         }
     }
 
+  if (message != NULL)
+    g_object_unref (message);
   g_object_unref (connection);
   g_free (filters);
 }
 
 /* Called in worker's thread */
-static gboolean
+static GDBusMessage *
 on_worker_message_about_to_be_sent (GDBusWorker  *worker,
                                     GDBusMessage *message,
                                     gpointer      user_data)
 {
-  GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
+  GDBusConnection *connection;
   FilterCallback *filters;
-  gboolean consumed_by_filter;
   guint num_filters;
   guint n;
+  gboolean alive;
 
-  //g_debug ("in on_worker_message_about_to_be_sent");
-
+  G_LOCK (message_bus_lock);
+  alive = (g_hash_table_lookup (alive_connections, user_data) != NULL);
+  if (!alive)
+    {
+      G_UNLOCK (message_bus_lock);
+      return message;
+    }
+  connection = G_DBUS_CONNECTION (user_data);
   g_object_ref (connection);
+  G_UNLOCK (message_bus_lock);
+
+  //g_debug ("in on_worker_message_about_to_be_sent");
 
   /* First collect the set of callback functions */
   CONNECTION_LOCK (connection);
@@ -2028,22 +2184,22 @@ on_worker_message_about_to_be_sent (GDBusWorker  *worker,
     }
   CONNECTION_UNLOCK (connection);
 
-  /* the call the filters in order (without holding the lock) */
-  consumed_by_filter = FALSE;
+  /* then call the filters in order (without holding the lock) */
   for (n = 0; n < num_filters; n++)
     {
-      consumed_by_filter = filters[n].func (connection,
-                                            message,
-                                            FALSE,
-                                            filters[n].user_data);
-      if (consumed_by_filter)
+      g_dbus_message_lock (message);
+      message = filters[n].func (connection,
+                                 message,
+                                 FALSE,
+                                 filters[n].user_data);
+      if (message == NULL)
         break;
     }
 
   g_object_unref (connection);
   g_free (filters);
 
-  return consumed_by_filter;
+  return message;
 }
 
 /* Called in worker's thread - we must not block */
@@ -2053,7 +2209,19 @@ on_worker_closed (GDBusWorker *worker,
                   GError      *error,
                   gpointer     user_data)
 {
-  GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
+  GDBusConnection *connection;
+  gboolean alive;
+
+  G_LOCK (message_bus_lock);
+  alive = (g_hash_table_lookup (alive_connections, user_data) != NULL);
+  if (!alive)
+    {
+      G_UNLOCK (message_bus_lock);
+      return;
+    }
+  connection = G_DBUS_CONNECTION (user_data);
+  g_object_ref (connection);
+  G_UNLOCK (message_bus_lock);
 
   //g_debug ("in on_worker_closed: %s", error->message);
 
@@ -2061,6 +2229,8 @@ on_worker_closed (GDBusWorker *worker,
   if (!connection->closed)
     set_closed_unlocked (connection, remote_peer_vanished, error);
   CONNECTION_UNLOCK (connection);
+
+  g_object_unref (connection);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -2159,7 +2329,7 @@ initable_init (GInitable     *initable,
                                     (connection->flags & G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS),
                                     get_offered_capabilities_max (connection),
                                     &connection->capabilities,
-                                    &connection->crendentials,
+                                    &connection->credentials,
                                     cancellable,
                                     &connection->initialization_error))
         goto out;
@@ -2201,6 +2371,12 @@ initable_init (GInitable     *initable,
     }
 #endif
 
+  G_LOCK (message_bus_lock);
+  if (alive_connections == NULL)
+    alive_connections = g_hash_table_new (g_direct_hash, g_direct_equal);
+  g_hash_table_insert (alive_connections, connection, connection);
+  G_UNLOCK (message_bus_lock);
+
   connection->worker = _g_dbus_worker_new (connection->stream,
                                            connection->capabilities,
                                            (connection->flags & G_DBUS_CONNECTION_FLAGS_DELAY_MESSAGE_PROCESSING),
@@ -2626,7 +2802,7 @@ g_dbus_connection_get_unique_name (GDBusConnection *connection)
  * each application is a client. So this method will always return
  * %NULL for message bus clients.
  *
- * Returns: A #GCredentials or %NULL if not available. Do not free
+ * Returns: (transfer none): A #GCredentials or %NULL if not available. Do not free
  * this object, it is owned by @connection.
  *
  * Since: 2.26
@@ -2635,7 +2811,7 @@ GCredentials *
 g_dbus_connection_get_peer_credentials (GDBusConnection *connection)
 {
   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
-  return connection->crendentials;
+  return connection->credentials;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -2655,7 +2831,9 @@ static guint _global_filter_id = 1;
  * are run in the order that they were added.  The same handler can be
  * added as a filter more than once, in which case it will be run more
  * than once.  Filters added during a filter callback won't be run on
- * the message being processed.
+ * the message being processed. Filter functions are allowed to modify
+ * and even drop messages - see the #GDBusMessageFilterResult
+ * enumeration for details.
  *
  * Note that filters are run in a dedicated message handling thread so
  * they can't block and, generally, can't do anything but signal a
@@ -2664,10 +2842,9 @@ static guint _global_filter_id = 1;
  * g_dbus_connection_signal_subscribe() or
  * g_dbus_connection_call() instead.
  *
- * If a filter consumes an incoming message (by returning %TRUE), the
- * message is not dispatched anywhere else - not even the standard
- * dispatch machinery (that API such as
- * g_dbus_connection_signal_subscribe() and
+ * If a filter consumes an incoming message the message is not
+ * dispatched anywhere else - not even the standard dispatch machinery
+ * (that API such as g_dbus_connection_signal_subscribe() and
  * g_dbus_connection_send_message_with_reply() relies on) will see the
  * message. Similary, if a filter consumes an outgoing message, the
  * message will not be sent to the other peer.
@@ -2801,11 +2978,14 @@ args_to_rule (const gchar *sender,
               const gchar *interface_name,
               const gchar *member,
               const gchar *object_path,
-              const gchar *arg0)
+              const gchar *arg0,
+              gboolean     negate)
 {
   GString *rule;
 
   rule = g_string_new ("type='signal'");
+  if (negate)
+    g_string_prepend_c (rule, '-');
   if (sender != NULL)
     g_string_append_printf (rule, ",sender='%s'", sender);
   if (interface_name != NULL)
@@ -2834,6 +3014,9 @@ add_match_rule (GDBusConnection *connection,
   GError *error;
   GDBusMessage *message;
 
+  if (match_rule[0] == '-')
+    return;
+
   message = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */
                                             "/org/freedesktop/DBus", /* path */
                                             "org.freedesktop.DBus", /* interface */
@@ -2842,6 +3025,7 @@ add_match_rule (GDBusConnection *connection,
   error = NULL;
   if (!g_dbus_connection_send_message_unlocked (connection,
                                                 message,
+                                                G_DBUS_SEND_MESSAGE_FLAGS_NONE,
                                                 NULL,
                                                 &error))
     {
@@ -2861,6 +3045,9 @@ remove_match_rule (GDBusConnection *connection,
   GError *error;
   GDBusMessage *message;
 
+  if (match_rule[0] == '-')
+    return;
+
   message = g_dbus_message_new_method_call ("org.freedesktop.DBus", /* name */
                                             "/org/freedesktop/DBus", /* path */
                                             "org.freedesktop.DBus", /* interface */
@@ -2870,6 +3057,7 @@ remove_match_rule (GDBusConnection *connection,
   error = NULL;
   if (!g_dbus_connection_send_message_unlocked (connection,
                                                 message,
+                                                G_DBUS_SEND_MESSAGE_FLAGS_NONE,
                                                 NULL,
                                                 &error))
     {
@@ -2963,7 +3151,16 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
 
   CONNECTION_LOCK (connection);
 
-  rule = args_to_rule (sender, interface_name, member, object_path, arg0);
+  /* If G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE was specified, we will end up
+   * with a '-' character to prefix the rule (which will otherwise be
+   * normal).
+   *
+   * This allows us to hash the rule and do our lifecycle tracking in
+   * the usual way, but the '-' prevents the match rule from ever
+   * actually being send to the bus (either for add or remove).
+   */
+  rule = args_to_rule (sender, interface_name, member, object_path, arg0,
+                       (flags & G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE) != 0);
 
   if (sender != NULL && (g_dbus_is_unique_name (sender) || g_strcmp0 (sender, "org.freedesktop.DBus") == 0))
     sender_unique_name = sender;
@@ -3036,7 +3233,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection     *connection,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-/* must hold lock when calling this */
+/* must hold lock when calling this (except if connection->finalizing is TRUE) */
 static void
 unsubscribe_id_internal (GDBusConnection *connection,
                          guint            subscription_id,
@@ -3086,7 +3283,7 @@ unsubscribe_id_internal (GDBusConnection *connection,
           if (connection->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)
             {
               if (!is_signal_data_for_name_lost_or_acquired (signal_data))
-                if (!connection->closed)
+                if (!connection->closed && !connection->finalizing)
                   remove_match_rule (connection, signal_data->rule);
             }
           signal_data_free (signal_data);
@@ -3135,8 +3332,9 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection,
     {
       SignalSubscriber *subscriber;
       subscriber = &(g_array_index (subscribers, SignalSubscriber, n));
-      if (subscriber->user_data_free_func != NULL)
-        subscriber->user_data_free_func (subscriber->user_data);
+      call_destroy_notify (subscriber->context,
+                           subscriber->user_data_free_func,
+                           subscriber->user_data);
       if (subscriber->context != NULL)
         g_main_context_unref (subscriber->context);
     }
@@ -3323,7 +3521,7 @@ distribute_signals (GDBusConnection *connection,
       _g_dbus_debug_print_lock ();
       g_print ("========================================================================\n"
                "GDBus-debug:Signal:\n"
-               " >>>> SIGNAL %s.%s\n"
+               " <<<< RECEIVED SIGNAL %s.%s\n"
                "      on object %s\n"
                "      sent by name %s\n",
                g_dbus_message_get_interface (message),
@@ -3382,8 +3580,9 @@ purge_all_signal_subscriptions (GDBusConnection *connection)
     {
       SignalSubscriber *subscriber;
       subscriber = &(g_array_index (subscribers, SignalSubscriber, n));
-      if (subscriber->user_data_free_func != NULL)
-        subscriber->user_data_free_func (subscriber->user_data);
+      call_destroy_notify (subscriber->context,
+                           subscriber->user_data_free_func,
+                           subscriber->user_data);
       if (subscriber->context != NULL)
         g_main_context_unref (subscriber->context);
     }
@@ -3465,9 +3664,9 @@ exported_interface_free (ExportedInterface *ei)
 {
   g_dbus_interface_info_unref ((GDBusInterfaceInfo *) ei->interface_info);
 
-  if (ei->user_data_free_func != NULL)
-    /* TODO: push to thread-default mainloop */
-    ei->user_data_free_func (ei->user_data);
+  call_destroy_notify (ei->context,
+                       ei->user_data_free_func,
+                       ei->user_data);
 
   if (ei->context != NULL)
     g_main_context_unref (ei->context);
@@ -3552,7 +3751,7 @@ invoke_get_property_in_idle_cb (gpointer _data)
                                                "org.freedesktop.DBus.Error.UnknownMethod",
                                                _("No such interface `org.freedesktop.DBus.Properties' on object at path %s"),
                                                g_dbus_message_get_path (data->message));
-      g_dbus_connection_send_message (data->connection, reply, NULL, NULL);
+      g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_object_unref (reply);
       goto out;
     }
@@ -3571,10 +3770,11 @@ invoke_get_property_in_idle_cb (gpointer _data)
     {
       g_assert_no_error (error);
 
-      g_variant_ref_sink (value);
+      if (g_variant_is_floating (value))
+        g_variant_ref_sink (value);
       reply = g_dbus_message_new_method_reply (data->message);
       g_dbus_message_set_body (reply, g_variant_new ("(v)", value));
-      g_dbus_connection_send_message (data->connection, reply, NULL, NULL);
+      g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_variant_unref (value);
       g_object_unref (reply);
     }
@@ -3586,7 +3786,7 @@ invoke_get_property_in_idle_cb (gpointer _data)
       reply = g_dbus_message_new_method_error_literal (data->message,
                                                        dbus_error_name,
                                                        error->message);
-      g_dbus_connection_send_message (data->connection, reply, NULL, NULL);
+      g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_free (dbus_error_name);
       g_error_free (error);
       g_object_unref (reply);
@@ -3653,8 +3853,9 @@ invoke_set_property_in_idle_cb (gpointer _data)
 
  out:
   g_assert (reply != NULL);
-  g_dbus_connection_send_message (data->connection, reply, NULL, NULL);
+  g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
   g_object_unref (reply);
+  g_variant_unref (value);
 
   return FALSE;
 }
@@ -3717,7 +3918,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection            *connect
                                                "org.freedesktop.DBus.Error.InvalidArgs",
                                                _("No such property `%s'"),
                                                property_name);
-      g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL);
+      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_object_unref (reply);
       handled = TRUE;
       goto out;
@@ -3729,7 +3930,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection            *connect
                                                "org.freedesktop.DBus.Error.InvalidArgs",
                                                _("Property `%s' is not readable"),
                                                property_name);
-      g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL);
+      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_object_unref (reply);
       handled = TRUE;
       goto out;
@@ -3740,7 +3941,7 @@ validate_and_maybe_schedule_property_getset (GDBusConnection            *connect
                                                "org.freedesktop.DBus.Error.InvalidArgs",
                                                _("Property `%s' is not writable"),
                                                property_name);
-      g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL);
+      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_object_unref (reply);
       handled = TRUE;
       goto out;
@@ -3810,7 +4011,7 @@ handle_getset_property (GDBusConnection *connection,
                                                "org.freedesktop.DBus.Error.InvalidArgs",
                                                _("No such interface `%s'"),
                                                interface_name);
-      g_dbus_connection_send_message_unlocked (eo->connection, reply, NULL, NULL);
+      g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_object_unref (reply);
       handled = TRUE;
       goto out;
@@ -3868,7 +4069,7 @@ invoke_get_all_properties_in_idle_cb (gpointer _data)
                                                "org.freedesktop.DBus.Error.UnknownMethod",
                                                _("No such interface `org.freedesktop.DBus.Properties' on object at path %s"),
                                                g_dbus_message_get_path (data->message));
-      g_dbus_connection_send_message (data->connection, reply, NULL, NULL);
+      g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_object_unref (reply);
       goto out;
     }
@@ -3902,16 +4103,19 @@ invoke_get_all_properties_in_idle_cb (gpointer _data)
       if (value == NULL)
         continue;
 
+      if (g_variant_is_floating (value))
+        g_variant_ref_sink (value);
       g_variant_builder_add (&builder,
                              "{sv}",
                              property_info->name,
                              value);
+      g_variant_unref (value);
     }
   g_variant_builder_close (&builder);
 
   reply = g_dbus_message_new_method_reply (data->message);
   g_dbus_message_set_body (reply, g_variant_builder_end (&builder));
-  g_dbus_connection_send_message (data->connection, reply, NULL, NULL);
+  g_dbus_connection_send_message (data->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
   g_object_unref (reply);
 
  out:
@@ -3995,7 +4199,7 @@ handle_get_all_properties (GDBusConnection *connection,
                                                "org.freedesktop.DBus.Error.InvalidArgs",
                                                _("No such interface"),
                                                interface_name);
-      g_dbus_connection_send_message_unlocked (eo->connection, reply, NULL, NULL);
+      g_dbus_connection_send_message_unlocked (eo->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_object_unref (reply);
       handled = TRUE;
       goto out;
@@ -4180,7 +4384,7 @@ handle_introspect (GDBusConnection *connection,
 
   reply = g_dbus_message_new_method_reply (message);
   g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str));
-  g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL);
+  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
   g_object_unref (reply);
   g_string_free (s, TRUE);
 
@@ -4212,7 +4416,7 @@ call_in_idle_cb (gpointer user_data)
                                                _("No such interface `%s' on object at path %s"),
                                                g_dbus_method_invocation_get_interface_name (invocation),
                                                g_dbus_method_invocation_get_object_path (invocation));
-      g_dbus_connection_send_message (g_dbus_method_invocation_get_connection (invocation), reply, NULL, NULL);
+      g_dbus_connection_send_message (g_dbus_method_invocation_get_connection (invocation), reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_object_unref (reply);
       goto out;
     }
@@ -4263,7 +4467,7 @@ validate_and_maybe_schedule_method_call (GDBusConnection            *connection,
                                                "org.freedesktop.DBus.Error.UnknownMethod",
                                                _("No such method `%s'"),
                                                g_dbus_message_get_member (message));
-      g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL);
+      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_object_unref (reply);
       handled = TRUE;
       goto out;
@@ -4295,7 +4499,7 @@ validate_and_maybe_schedule_method_call (GDBusConnection            *connection,
                                                _("Type of message, `%s', does not match expected type `%s'"),
                                                g_variant_get_type_string (parameters),
                                                type_string);
-      g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL);
+      g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_variant_type_free (in_type);
       g_variant_unref (parameters);
       g_object_unref (reply);
@@ -4306,15 +4510,15 @@ validate_and_maybe_schedule_method_call (GDBusConnection            *connection,
   g_variant_type_free (in_type);
 
   /* schedule the call in idle */
-  invocation = g_dbus_method_invocation_new (g_dbus_message_get_sender (message),
-                                             g_dbus_message_get_path (message),
-                                             g_dbus_message_get_interface (message),
-                                             g_dbus_message_get_member (message),
-                                             method_info,
-                                             connection,
-                                             message,
-                                             parameters,
-                                             user_data);
+  invocation = _g_dbus_method_invocation_new (g_dbus_message_get_sender (message),
+                                              g_dbus_message_get_path (message),
+                                              g_dbus_message_get_interface (message),
+                                              g_dbus_message_get_member (message),
+                                              method_info,
+                                              connection,
+                                              message,
+                                              parameters,
+                                              user_data);
   g_variant_unref (parameters);
 
   /* TODO: would be nicer with a real MethodData like we already
@@ -4661,7 +4865,7 @@ g_dbus_connection_emit_signal (GDBusConnection  *connection,
   if (parameters != NULL)
     g_dbus_message_set_body (message, parameters);
 
-  ret = g_dbus_connection_send_message (connection, message, NULL, error);
+  ret = g_dbus_connection_send_message (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, error);
   g_object_unref (message);
 
   return ret;
@@ -4756,7 +4960,7 @@ g_dbus_connection_call_done (GObject      *source,
       _g_dbus_debug_print_lock ();
       g_print ("========================================================================\n"
                "GDBus-debug:Call:\n"
-               " >>>> ASYNC COMPLETE %s() (serial %d)\n"
+               " <<<< ASYNC COMPLETE %s() (serial %d)\n"
                "      ",
                state->method_name,
                state->serial);
@@ -4783,10 +4987,7 @@ g_dbus_connection_call_done (GObject      *source,
     value = NULL;
 
   if (value == NULL)
-    {
-      g_simple_async_result_set_from_error (state->simple, error);
-      g_error_free (error);
-    }
+    g_simple_async_result_take_error (state->simple, error);
   else
     g_simple_async_result_set_op_res_gpointer (state->simple, value,
                                                (GDestroyNotify) g_variant_unref);
@@ -4809,7 +5010,8 @@ g_dbus_connection_call_done (GObject      *source,
  * @parameters: A #GVariant tuple with parameters for the method or %NULL if not passing parameters.
  * @reply_type: The expected type of the reply, or %NULL.
  * @flags: Flags from the #GDBusCallFlags enumeration.
- * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout.
+ * @timeout_msec: The timeout in milliseconds, -1 to use the default
+ *                timeout or %G_MAXINT for no timeout.
  * @cancellable: A #GCancellable or %NULL.
  * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't
  * care about the result of the method invocation.
@@ -4903,6 +5105,7 @@ g_dbus_connection_call (GDBusConnection        *connection,
 
   g_dbus_connection_send_message_with_reply (connection,
                                              message,
+                                             G_DBUS_SEND_MESSAGE_FLAGS_NONE,
                                              timeout_msec,
                                              &state->serial,
                                              cancellable,
@@ -4974,7 +5177,8 @@ g_dbus_connection_call_finish (GDBusConnection  *connection,
  * @parameters: A #GVariant tuple with parameters for the method or %NULL if not passing parameters.
  * @reply_type: The expected type of the reply, or %NULL.
  * @flags: Flags from the #GDBusCallFlags enumeration.
- * @timeout_msec: The timeout in milliseconds or -1 to use the default timeout.
+ * @timeout_msec: The timeout in milliseconds, -1 to use the default
+ *                timeout or %G_MAXINT for no timeout.
  * @cancellable: A #GCancellable or %NULL.
  * @error: Return location for error or %NULL.
  *
@@ -5079,6 +5283,7 @@ g_dbus_connection_call_sync (GDBusConnection         *connection,
   local_error = NULL;
   reply = g_dbus_connection_send_message_with_reply_sync (connection,
                                                           message,
+                                                         G_DBUS_SEND_MESSAGE_FLAGS_NONE,
                                                           timeout_msec,
                                                           NULL, /* volatile guint32 *out_serial */
                                                           cancellable,
@@ -5143,9 +5348,9 @@ struct ExportedSubtree
 static void
 exported_subtree_free (ExportedSubtree *es)
 {
-  if (es->user_data_free_func != NULL)
-    /* TODO: push to thread-default mainloop */
-    es->user_data_free_func (es->user_data);
+  call_destroy_notify (es->context,
+                       es->user_data_free_func,
+                       es->user_data);
 
   if (es->context != NULL)
     g_main_context_unref (es->context);
@@ -5238,7 +5443,7 @@ handle_subtree_introspect (GDBusConnection *connection,
 
   reply = g_dbus_message_new_method_reply (message);
   g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str));
-  g_dbus_connection_send_message (connection, reply, NULL, NULL);
+  g_dbus_connection_send_message (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
   g_object_unref (reply);
 
   handled = TRUE;
@@ -5396,7 +5601,7 @@ handle_subtree_method_invocation (GDBusConnection *connection,
                                                    "org.freedesktop.DBus.Error.InvalidArgs",
                                                    _("No such interface `%s'"),
                                                    interface_name);
-          g_dbus_connection_send_message (es->connection, reply, NULL, NULL);
+          g_dbus_connection_send_message (es->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
           g_object_unref (reply);
           handled = TRUE;
           goto out;
@@ -5509,7 +5714,7 @@ process_subtree_vtable_message_in_idle_cb (gpointer _data)
                                                g_dbus_message_get_member (data->message),
                                                g_dbus_message_get_interface (data->message),
                                                g_dbus_message_get_signature (data->message));
-      g_dbus_connection_send_message (data->es->connection, reply, NULL, NULL);
+      g_dbus_connection_send_message (data->es->connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
       g_object_unref (reply);
     }
 
@@ -5706,7 +5911,7 @@ handle_generic_ping_unlocked (GDBusConnection *connection,
 {
   GDBusMessage *reply;
   reply = g_dbus_message_new_method_reply (message);
-  g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL);
+  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
   g_object_unref (reply);
 }
 
@@ -5739,7 +5944,7 @@ handle_generic_get_machine_id_unlocked (GDBusConnection *connection,
       reply = g_dbus_message_new_method_reply (message);
       g_dbus_message_set_body (reply, g_variant_new ("(s)", connection->machine_id));
     }
-  g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL);
+  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
   g_object_unref (reply);
 }
 
@@ -5766,7 +5971,7 @@ handle_generic_introspect_unlocked (GDBusConnection *connection,
 
   reply = g_dbus_message_new_method_reply (message);
   g_dbus_message_set_body (reply, g_variant_new ("(s)", s->str));
-  g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL);
+  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
   g_object_unref (reply);
   g_string_free (s, TRUE);
 }
@@ -5858,12 +6063,14 @@ distribute_method_call (GDBusConnection *connection,
       _g_dbus_debug_print_lock ();
       g_print ("========================================================================\n"
                "GDBus-debug:Incoming:\n"
-               " >>>> METHOD INVOCATION %s.%s()\n"
+               " <<<< METHOD INVOCATION %s.%s()\n"
                "      on object %s\n"
-               "      invoked by name %s\n",
+               "      invoked by name %s\n"
+               "      serial %d\n",
                interface_name, member,
                path,
-               g_dbus_message_get_sender (message) != NULL ? g_dbus_message_get_sender (message) : "(none)");
+               g_dbus_message_get_sender (message) != NULL ? g_dbus_message_get_sender (message) : "(none)",
+               g_dbus_message_get_serial (message));
       _g_dbus_debug_print_unlock ();
     }
 
@@ -5911,7 +6118,7 @@ distribute_method_call (GDBusConnection *connection,
                                            _("No such interface `%s' on object at path %s"),
                                            interface_name,
                                            object_path);
-  g_dbus_connection_send_message_unlocked (connection, reply, NULL, NULL);
+  g_dbus_connection_send_message_unlocked (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, NULL);
   g_object_unref (reply);
 
  out:
@@ -6046,7 +6253,7 @@ get_uninitialized_connection (GBusType       bus_type,
  * Note that the returned #GDBusConnection object will (usually) have
  * the #GDBusConnection:exit-on-close property set to %TRUE.
  *
- * Returns: A #GDBusConnection or %NULL if @error is set. Free with g_object_unref().
+ * Returns: (transfer full): A #GDBusConnection or %NULL if @error is set. Free with g_object_unref().
  *
  * Since: 2.26
  */
@@ -6087,8 +6294,7 @@ bus_get_async_initable_cb (GObject      *source_object,
                                      &error))
     {
       g_assert (error != NULL);
-      g_simple_async_result_set_from_error (simple, error);
-      g_error_free (error);
+      g_simple_async_result_take_error (simple, error);
       g_object_unref (source_object);
     }
   else
@@ -6138,8 +6344,7 @@ g_bus_get (GBusType             bus_type,
   if (connection == NULL)
     {
       g_assert (error != NULL);
-      g_simple_async_result_set_from_error (simple, error);
-      g_error_free (error);
+      g_simple_async_result_take_error (simple, error);
       g_simple_async_result_complete_in_idle (simple);
       g_object_unref (simple);
     }
@@ -6169,7 +6374,7 @@ 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: 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
  */