Merge branch 'dbus-1.4'
[platform/upstream/dbus.git] / dbus / dbus-connection.c
index 7f828c5..648687a 100644 (file)
@@ -38,6 +38,7 @@
 #include "dbus-protocol.h"
 #include "dbus-dataslot.h"
 #include "dbus-string.h"
+#include "dbus-signature.h"
 #include "dbus-pending-call.h"
 #include "dbus-object-tree.h"
 #include "dbus-threads-internal.h"
     _dbus_mutex_unlock ((connection)->mutex);                                            \
   } while (0)
 
+#define SLOTS_LOCK(connection) do {                     \
+    _dbus_mutex_lock ((connection)->slot_mutex);        \
+  } while (0)
+
+#define SLOTS_UNLOCK(connection) do {                   \
+    _dbus_mutex_unlock ((connection)->slot_mutex);      \
+  } while (0)
+
 #define DISPATCH_STATUS_NAME(s)                                            \
                      ((s) == DBUS_DISPATCH_COMPLETE ? "complete" :         \
                       (s) == DBUS_DISPATCH_DATA_REMAINS ? "data remains" : \
@@ -225,7 +234,7 @@ struct DBusPreallocatedSend
   DBusList *counter_link;     /**< Preallocated link in the resource counter */
 };
 
-#ifdef HAVE_DECL_MSG_NOSIGNAL
+#if HAVE_DECL_MSG_NOSIGNAL
 static dbus_bool_t _dbus_modify_sigpipe = FALSE;
 #else
 static dbus_bool_t _dbus_modify_sigpipe = TRUE;
@@ -263,6 +272,7 @@ struct DBusConnection
   
   DBusList *filter_list;        /**< List of filters. */
 
+  DBusMutex *slot_mutex;        /**< Lock on slot_list so overall connection lock need not be taken */
   DBusDataSlotList slot_list;   /**< Data stored by allocated integer ID */
 
   DBusHashTable *pending_replies;  /**< Hash of message serials to #DBusPendingCall. */  
@@ -655,39 +665,42 @@ protected_change_watch (DBusConnection         *connection,
                         DBusWatchToggleFunction toggle_function,
                         dbus_bool_t             enabled)
 {
-  DBusWatchList *watches;
   dbus_bool_t retval;
-  
+
   HAVE_LOCK_CHECK (connection);
 
-  /* This isn't really safe or reasonable; a better pattern is the "do everything, then
-   * drop lock and call out" one; but it has to be propagated up through all callers
+  /* The original purpose of protected_change_watch() was to hold a
+   * ref on the connection while dropping the connection lock, then
+   * calling out to the app.  This was a broken hack that did not
+   * work, since the connection was in a hosed state (no WatchList
+   * field) while calling out.
+   *
+   * So for now we'll just keep the lock while calling out. This means
+   * apps are not allowed to call DBusConnection methods inside a
+   * watch function or they will deadlock.
+   *
+   * The "real fix" is to use the _and_unlock() pattern found
+   * elsewhere in the code, to defer calling out to the app until
+   * we're about to drop locks and return flow of control to the app
+   * anyway.
+   *
+   * See http://lists.freedesktop.org/archives/dbus/2007-July/thread.html#8144
    */
-  
-  watches = connection->watches;
-  if (watches)
-    {
-      connection->watches = NULL;
-      _dbus_connection_ref_unlocked (connection);
-      CONNECTION_UNLOCK (connection);
 
+  if (connection->watches)
+    {
       if (add_function)
-        retval = (* add_function) (watches, watch);
+        retval = (* add_function) (connection->watches, watch);
       else if (remove_function)
         {
           retval = TRUE;
-          (* remove_function) (watches, watch);
+          (* remove_function) (connection->watches, watch);
         }
       else
         {
           retval = TRUE;
-          (* toggle_function) (watches, watch, enabled);
+          (* toggle_function) (connection->watches, watch, enabled);
         }
-      
-      CONNECTION_LOCK (connection);
-      connection->watches = watches;
-      _dbus_connection_unref_unlocked (connection);
-
       return retval;
     }
   else
@@ -776,39 +789,42 @@ protected_change_timeout (DBusConnection           *connection,
                           DBusTimeoutToggleFunction toggle_function,
                           dbus_bool_t               enabled)
 {
-  DBusTimeoutList *timeouts;
   dbus_bool_t retval;
-  
+
   HAVE_LOCK_CHECK (connection);
 
-  /* This isn't really safe or reasonable; a better pattern is the "do everything, then
-   * drop lock and call out" one; but it has to be propagated up through all callers
+  /* The original purpose of protected_change_timeout() was to hold a
+   * ref on the connection while dropping the connection lock, then
+   * calling out to the app.  This was a broken hack that did not
+   * work, since the connection was in a hosed state (no TimeoutList
+   * field) while calling out.
+   *
+   * So for now we'll just keep the lock while calling out. This means
+   * apps are not allowed to call DBusConnection methods inside a
+   * timeout function or they will deadlock.
+   *
+   * The "real fix" is to use the _and_unlock() pattern found
+   * elsewhere in the code, to defer calling out to the app until
+   * we're about to drop locks and return flow of control to the app
+   * anyway.
+   *
+   * See http://lists.freedesktop.org/archives/dbus/2007-July/thread.html#8144
    */
-  
-  timeouts = connection->timeouts;
-  if (timeouts)
-    {
-      connection->timeouts = NULL;
-      _dbus_connection_ref_unlocked (connection);
-      CONNECTION_UNLOCK (connection);
 
+  if (connection->timeouts)
+    {
       if (add_function)
-        retval = (* add_function) (timeouts, timeout);
+        retval = (* add_function) (connection->timeouts, timeout);
       else if (remove_function)
         {
           retval = TRUE;
-          (* remove_function) (timeouts, timeout);
+          (* remove_function) (connection->timeouts, timeout);
         }
       else
         {
           retval = TRUE;
-          (* toggle_function) (timeouts, timeout, enabled);
+          (* toggle_function) (connection->timeouts, timeout, enabled);
         }
-      
-      CONNECTION_LOCK (connection);
-      connection->timeouts = timeouts;
-      _dbus_connection_unref_unlocked (connection);
-
       return retval;
     }
   else
@@ -1269,6 +1285,10 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
   if (connection->io_path_cond == NULL)
     goto error;
 
+  _dbus_mutex_new_at_location (&connection->slot_mutex);
+  if (connection->slot_mutex == NULL)
+    goto error;
+
   disconnect_message = dbus_message_new_signal (DBUS_PATH_LOCAL,
                                                 DBUS_INTERFACE_LOCAL,
                                                 "Disconnected");
@@ -1345,6 +1365,7 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
       _dbus_mutex_free_at_location (&connection->mutex);
       _dbus_mutex_free_at_location (&connection->io_path_mutex);
       _dbus_mutex_free_at_location (&connection->dispatch_mutex);
+      _dbus_mutex_free_at_location (&connection->slot_mutex);
       dbus_free (connection);
     }
   if (pending_replies)
@@ -1379,13 +1400,8 @@ _dbus_connection_ref_unlocked (DBusConnection *connection)
   _dbus_assert (connection->generation == _dbus_current_generation);
 
   HAVE_LOCK_CHECK (connection);
-  
-#ifdef DBUS_HAVE_ATOMIC_INT
+
   _dbus_atomic_inc (&connection->refcount);
-#else
-  _dbus_assert (connection->refcount.value > 0);
-  connection->refcount.value += 1;
-#endif
 
   return connection;
 }
@@ -1405,22 +1421,8 @@ _dbus_connection_unref_unlocked (DBusConnection *connection)
   
   _dbus_assert (connection != NULL);
 
-  /* The connection lock is better than the global
-   * lock in the atomic increment fallback
-   */
-  
-#ifdef DBUS_HAVE_ATOMIC_INT
   last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
-#else
-  _dbus_assert (connection->refcount.value > 0);
 
-  connection->refcount.value -= 1;
-  last_unref = (connection->refcount.value == 0);  
-#if 0
-  printf ("unref_unlocked() connection %p count = %d\n", connection, connection->refcount.value);
-#endif
-#endif
-  
   if (last_unref)
     _dbus_connection_last_unref (connection);
 }
@@ -1960,7 +1962,6 @@ _dbus_connection_send_preallocated_unlocked_no_update (DBusConnection       *con
                                                        dbus_uint32_t        *client_serial)
 {
   dbus_uint32_t serial;
-  const char *sig;
 
   preallocated->queue_link->data = message;
   _dbus_list_prepend_link (&connection->outgoing_messages,
@@ -1976,8 +1977,6 @@ _dbus_connection_send_preallocated_unlocked_no_update (DBusConnection       *con
   
   connection->n_outgoing += 1;
 
-  sig = dbus_message_get_signature (message);
-  
   _dbus_verbose ("Message %p (%s %s %s %s '%s') for %s added to outgoing queue %p, %d pending to send\n",
                  message,
                  dbus_message_type_to_string (dbus_message_get_type (message)),
@@ -1990,7 +1989,7 @@ _dbus_connection_send_preallocated_unlocked_no_update (DBusConnection       *con
                  dbus_message_get_member (message) ?
                  dbus_message_get_member (message) :
                  "no member",
-                 sig,
+                 dbus_message_get_signature (message),
                  dbus_message_get_destination (message) ?
                  dbus_message_get_destination (message) :
                  "null",
@@ -2109,11 +2108,23 @@ _dbus_connection_send_and_unlock (DBusConnection *connection,
 void
 _dbus_connection_close_if_only_one_ref (DBusConnection *connection)
 {
+  dbus_int32_t tmp_refcount;
+
   CONNECTION_LOCK (connection);
-  
-  _dbus_assert (connection->refcount.value > 0);
 
-  if (connection->refcount.value == 1)
+  /* We increment and then decrement the refcount, because there is no
+   * _dbus_atomic_get (mirroring the fact that there's no InterlockedGet
+   * on Windows). */
+  _dbus_atomic_inc (&connection->refcount);
+  tmp_refcount = _dbus_atomic_dec (&connection->refcount);
+
+  /* The caller should have one ref, and this function temporarily took
+   * one more, which is reflected in this count even though we already
+   * released it (relying on the caller's ref) due to _dbus_atomic_dec
+   * semantics */
+  _dbus_assert (tmp_refcount >= 2);
+
+  if (tmp_refcount == 2)
     _dbus_connection_close_possibly_shared_and_unlock (connection);
   else
     CONNECTION_UNLOCK (connection);
@@ -2365,10 +2376,10 @@ _dbus_connection_block_pending_call (DBusPendingCall *pending)
    * below
    */
   timeout = _dbus_pending_call_get_timeout_unlocked (pending);
+  _dbus_get_current_time (&start_tv_sec, &start_tv_usec);
   if (timeout)
     {
       timeout_milliseconds = dbus_timeout_get_interval (timeout);
-      _dbus_get_current_time (&start_tv_sec, &start_tv_usec);
 
       _dbus_verbose ("dbus_connection_send_with_reply_and_block(): will block %d milliseconds for reply serial %u from %ld sec %ld usec\n",
                      timeout_milliseconds,
@@ -2615,20 +2626,8 @@ dbus_connection_ref (DBusConnection *connection)
 {
   _dbus_return_val_if_fail (connection != NULL, NULL);
   _dbus_return_val_if_fail (connection->generation == _dbus_current_generation, NULL);
-  
-  /* The connection lock is better than the global
-   * lock in the atomic increment fallback
-   */
-  
-#ifdef DBUS_HAVE_ATOMIC_INT
-  _dbus_atomic_inc (&connection->refcount);
-#else
-  CONNECTION_LOCK (connection);
-  _dbus_assert (connection->refcount.value > 0);
 
-  connection->refcount.value += 1;
-  CONNECTION_UNLOCK (connection);
-#endif
+  _dbus_atomic_inc (&connection->refcount);
 
   return connection;
 }
@@ -2732,6 +2731,8 @@ _dbus_connection_last_unref (DBusConnection *connection)
   _dbus_mutex_free_at_location (&connection->io_path_mutex);
   _dbus_mutex_free_at_location (&connection->dispatch_mutex);
 
+  _dbus_mutex_free_at_location (&connection->slot_mutex);
+
   _dbus_mutex_free_at_location (&connection->mutex);
   
   dbus_free (connection);
@@ -2763,28 +2764,9 @@ dbus_connection_unref (DBusConnection *connection)
 
   _dbus_return_if_fail (connection != NULL);
   _dbus_return_if_fail (connection->generation == _dbus_current_generation);
-  
-  /* The connection lock is better than the global
-   * lock in the atomic increment fallback
-   */
-  
-#ifdef DBUS_HAVE_ATOMIC_INT
-  last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
-#else
-  CONNECTION_LOCK (connection);
-  
-  _dbus_assert (connection->refcount.value > 0);
 
-  connection->refcount.value -= 1;
-  last_unref = (connection->refcount.value == 0);
+  last_unref = (_dbus_atomic_dec (&connection->refcount) == 1);
 
-#if 0
-  printf ("unref() connection %p count = %d\n", connection, connection->refcount.value);
-#endif
-  
-  CONNECTION_UNLOCK (connection);
-#endif
-  
   if (last_unref)
     {
 #ifndef DBUS_DISABLE_CHECKS
@@ -3054,7 +3036,7 @@ dbus_connection_get_server_id (DBusConnection *connection)
  * This function can be used to do runtime checking for types that
  * might be unknown to the specific D-Bus client implementation
  * version, i.e. it will return FALSE for all types this
- * implementation does not know.
+ * implementation does not know, including invalid or reserved types.
  *
  * @param connection the connection
  * @param type the type to check
@@ -3066,7 +3048,7 @@ dbus_connection_can_send_type(DBusConnection *connection,
 {
   _dbus_return_val_if_fail (connection != NULL, FALSE);
 
-  if (!_dbus_type_is_valid(type))
+  if (!dbus_type_is_valid (type))
     return FALSE;
 
   if (type != DBUS_TYPE_UNIX_FD)
@@ -3294,6 +3276,7 @@ reply_handler_timeout (void *data)
   DBusPendingCall *pending = data;
 
   connection = _dbus_pending_call_get_connection_and_lock (pending);
+  _dbus_connection_ref_unlocked (connection);
 
   _dbus_pending_call_queue_timeout_error_unlocked (pending, 
                                                    connection);
@@ -3306,6 +3289,7 @@ reply_handler_timeout (void *data)
 
   /* Unlocks, and calls out to user code */
   _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+  dbus_connection_unref (connection);
   
   return TRUE;
 }
@@ -3332,8 +3316,9 @@ reply_handler_timeout (void *data)
  *
  * If -1 is passed for the timeout, a sane default timeout is used. -1
  * is typically the best value for the timeout for this reason, unless
- * you want a very short or very long timeout.  If INT_MAX is passed for
- * the timeout, no timeout will be set and the call will block forever.
+ * you want a very short or very long timeout.  If #DBUS_TIMEOUT_INFINITE is
+ * passed for the timeout, no timeout will be set and the call will block
+ * forever.
  *
  * @warning if the connection is disconnected or you try to send Unix
  * file descriptors on a connection that does not support them, the
@@ -3345,7 +3330,9 @@ reply_handler_timeout (void *data)
  * object, or #NULL if connection is disconnected or when you try to
  * send Unix file descriptors on a connection that does not support
  * them.
- * @param timeout_milliseconds timeout in milliseconds, -1 for default or INT_MAX for no timeout
+ * @param timeout_milliseconds timeout in milliseconds, -1 (or
+ *  #DBUS_TIMEOUT_USE_DEFAULT) for default or #DBUS_TIMEOUT_INFINITE for no
+ *  timeout
  * @returns #FALSE if no memory, #TRUE otherwise.
  *
  */
@@ -3478,7 +3465,9 @@ dbus_connection_send_with_reply (DBusConnection     *connection,
  *
  * @param connection the connection
  * @param message the message to send
- * @param timeout_milliseconds timeout in milliseconds, -1 for default or INT_MAX for no timeout.
+ * @param timeout_milliseconds timeout in milliseconds, -1 (or
+ *  #DBUS_TIMEOUT_USE_DEFAULT) for default or #DBUS_TIMEOUT_INFINITE for no
+ *  timeout
  * @param error return location for error message
  * @returns the message that is the reply or #NULL with an error code if the
  * function fails.
@@ -4493,6 +4482,7 @@ dbus_connection_dispatch (DBusConnection *connection)
   DBusPendingCall *pending;
   dbus_int32_t reply_serial;
   DBusDispatchStatus status;
+  dbus_bool_t found_object;
 
   _dbus_return_val_if_fail (connection != NULL, DBUS_DISPATCH_COMPLETE);
 
@@ -4584,9 +4574,6 @@ dbus_connection_dispatch (DBusConnection *connection)
       /* unlocks and calls user code */
       _dbus_connection_update_dispatch_status_and_unlock (connection,
                                                           DBUS_DISPATCH_NEED_MEMORY);
-
-      if (pending)
-        dbus_pending_call_unref (pending);
       dbus_connection_unref (connection);
       
       return DBUS_DISPATCH_NEED_MEMORY;
@@ -4657,7 +4644,8 @@ dbus_connection_dispatch (DBusConnection *connection)
 
   HAVE_LOCK_CHECK (connection);
   result = _dbus_object_tree_dispatch_and_unlock (connection->objects,
-                                                  message);
+                                                  message,
+                                                  &found_object);
   
   CONNECTION_LOCK (connection);
 
@@ -4696,7 +4684,7 @@ dbus_connection_dispatch (DBusConnection *connection)
         }
       
       reply = dbus_message_new_error (message,
-                                      DBUS_ERROR_UNKNOWN_METHOD,
+                                      found_object ? DBUS_ERROR_UNKNOWN_METHOD : DBUS_ERROR_UNKNOWN_OBJECT,
                                       _dbus_string_get_const_data (&str));
       _dbus_string_free (&str);
 
@@ -4819,9 +4807,11 @@ dbus_connection_dispatch (DBusConnection *connection)
  * should be that dbus_connection_set_watch_functions() has no effect,
  * but the add_function and remove_function may have been called.
  *
- * @todo We need to drop the lock when we call the
- * add/remove/toggled functions which can be a side effect
- * of setting the watch functions.
+ * @note The thread lock on DBusConnection is held while
+ * watch functions are invoked, so inside these functions you
+ * may not invoke any methods on DBusConnection or it will deadlock.
+ * See the comments in the code or http://lists.freedesktop.org/archives/dbus/2007-July/tread.html#8144
+ * if you encounter this issue and want to attempt writing a patch.
  * 
  * @param connection the connection.
  * @param add_function function to begin monitoring a new descriptor.
@@ -4840,42 +4830,18 @@ dbus_connection_set_watch_functions (DBusConnection              *connection,
                                      DBusFreeFunction             free_data_function)
 {
   dbus_bool_t retval;
-  DBusWatchList *watches;
 
   _dbus_return_val_if_fail (connection != NULL, FALSE);
   
   CONNECTION_LOCK (connection);
 
-#ifndef DBUS_DISABLE_CHECKS
-  if (connection->watches == NULL)
-    {
-      _dbus_warn_check_failed ("Re-entrant call is not allowed\n");
-      return FALSE;
-    }
-#endif
-  
-  /* ref connection for slightly better reentrancy */
-  _dbus_connection_ref_unlocked (connection);
-
-  /* This can call back into user code, and we need to drop the
-   * connection lock when it does. This is kind of a lame
-   * way to do it.
-   */
-  watches = connection->watches;
-  connection->watches = NULL;
-  CONNECTION_UNLOCK (connection);
-
-  retval = _dbus_watch_list_set_functions (watches,
+  retval = _dbus_watch_list_set_functions (connection->watches,
                                            add_function, remove_function,
                                            toggled_function,
                                            data, free_data_function);
-  CONNECTION_LOCK (connection);
-  connection->watches = watches;
-  
+
   CONNECTION_UNLOCK (connection);
-  /* drop our paranoid refcount */
-  dbus_connection_unref (connection);
-  
+
   return retval;
 }
 
@@ -4904,6 +4870,12 @@ dbus_connection_set_watch_functions (DBusConnection              *connection,
  * given remove_function.  The timer interval may change whenever the
  * timeout is added, removed, or toggled.
  *
+ * @note The thread lock on DBusConnection is held while
+ * timeout functions are invoked, so inside these functions you
+ * may not invoke any methods on DBusConnection or it will deadlock.
+ * See the comments in the code or http://lists.freedesktop.org/archives/dbus/2007-July/thread.html#8144
+ * if you encounter this issue and want to attempt writing a patch.
+ *
  * @param connection the connection.
  * @param add_function function to add a timeout.
  * @param remove_function function to remove a timeout.
@@ -4921,37 +4893,17 @@ dbus_connection_set_timeout_functions   (DBusConnection            *connection,
                                         DBusFreeFunction           free_data_function)
 {
   dbus_bool_t retval;
-  DBusTimeoutList *timeouts;
 
   _dbus_return_val_if_fail (connection != NULL, FALSE);
   
   CONNECTION_LOCK (connection);
 
-#ifndef DBUS_DISABLE_CHECKS
-  if (connection->timeouts == NULL)
-    {
-      _dbus_warn_check_failed ("Re-entrant call is not allowed\n");
-      return FALSE;
-    }
-#endif
-  
-  /* ref connection for slightly better reentrancy */
-  _dbus_connection_ref_unlocked (connection);
-
-  timeouts = connection->timeouts;
-  connection->timeouts = NULL;
-  CONNECTION_UNLOCK (connection);
-  
-  retval = _dbus_timeout_list_set_functions (timeouts,
+  retval = _dbus_timeout_list_set_functions (connection->timeouts,
                                              add_function, remove_function,
                                              toggled_function,
                                              data, free_data_function);
-  CONNECTION_LOCK (connection);
-  connection->timeouts = timeouts;
-  
+
   CONNECTION_UNLOCK (connection);
-  /* drop our paranoid refcount */
-  dbus_connection_unref (connection);
 
   return retval;
 }
@@ -5569,7 +5521,7 @@ dbus_connection_remove_filter (DBusConnection            *connection,
  * @param user_data data to pass to functions in the vtable
  * @param error address where an error can be returned
  * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
- *    #DBUS_ERROR_ADDRESS_IN_USE) is reported
+ *    #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
  */
 dbus_bool_t
 dbus_connection_try_register_object_path (DBusConnection              *connection,
@@ -5615,7 +5567,8 @@ dbus_connection_try_register_object_path (DBusConnection              *connectio
  * @param path a '/' delimited string of path elements
  * @param vtable the virtual table
  * @param user_data data to pass to functions in the vtable
- * @returns #FALSE if not enough memory
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ *    #DBUS_ERROR_OBJECT_PATH_IN_USE) ocurred
  */
 dbus_bool_t
 dbus_connection_register_object_path (DBusConnection              *connection,
@@ -5646,7 +5599,7 @@ dbus_connection_register_object_path (DBusConnection              *connection,
 
   dbus_free_string_array (decomposed_path);
 
-  if (dbus_error_has_name (&error, DBUS_ERROR_ADDRESS_IN_USE))
+  if (dbus_error_has_name (&error, DBUS_ERROR_OBJECT_PATH_IN_USE))
     {
       _dbus_warn ("%s\n", error.message);
       dbus_error_free (&error);
@@ -5668,7 +5621,7 @@ dbus_connection_register_object_path (DBusConnection              *connection,
  * @param user_data data to pass to functions in the vtable
  * @param error address where an error can be returned
  * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
- *    #DBUS_ERROR_ADDRESS_IN_USE) is reported
+ *    #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
  */
 dbus_bool_t
 dbus_connection_try_register_fallback (DBusConnection              *connection,
@@ -5716,7 +5669,8 @@ dbus_connection_try_register_fallback (DBusConnection              *connection,
  * @param path a '/' delimited string of path elements
  * @param vtable the virtual table
  * @param user_data data to pass to functions in the vtable
- * @returns #FALSE if not enough memory
+ * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
+ *    #DBUS_ERROR_OBJECT_PATH_IN_USE) occured
  */
 dbus_bool_t
 dbus_connection_register_fallback (DBusConnection              *connection,
@@ -5747,7 +5701,7 @@ dbus_connection_register_fallback (DBusConnection              *connection,
 
   dbus_free_string_array (decomposed_path);
 
-  if (dbus_error_has_name (&error, DBUS_ERROR_ADDRESS_IN_USE))
+  if (dbus_error_has_name (&error, DBUS_ERROR_OBJECT_PATH_IN_USE))
     {
       _dbus_warn ("%s\n", error.message);
       dbus_error_free (&error);
@@ -5911,6 +5865,15 @@ dbus_connection_free_data_slot (dbus_int32_t *slot_p)
  * the connection is finalized. The slot number
  * must have been allocated with dbus_connection_allocate_data_slot().
  *
+ * @note This function does not take the
+ * main thread lock on DBusConnection, which allows it to be
+ * used from inside watch and timeout functions. (See the
+ * note in docs for dbus_connection_set_watch_functions().)
+ * A side effect of this is that you need to know there's
+ * a reference held on the connection while invoking
+ * dbus_connection_set_data(), or the connection could be
+ * finalized during dbus_connection_set_data().
+ *
  * @param connection the connection
  * @param slot the slot number
  * @param data the data to store
@@ -5930,14 +5893,14 @@ dbus_connection_set_data (DBusConnection   *connection,
   _dbus_return_val_if_fail (connection != NULL, FALSE);
   _dbus_return_val_if_fail (slot >= 0, FALSE);
   
-  CONNECTION_LOCK (connection);
+  SLOTS_LOCK (connection);
 
   retval = _dbus_data_slot_list_set (&slot_allocator,
                                      &connection->slot_list,
                                      slot, data, free_data_func,
                                      &old_free_func, &old_data);
   
-  CONNECTION_UNLOCK (connection);
+  SLOTS_UNLOCK (connection);
 
   if (retval)
     {
@@ -5953,6 +5916,15 @@ dbus_connection_set_data (DBusConnection   *connection,
  * Retrieves data previously set with dbus_connection_set_data().
  * The slot must still be allocated (must not have been freed).
  *
+ * @note This function does not take the
+ * main thread lock on DBusConnection, which allows it to be
+ * used from inside watch and timeout functions. (See the
+ * note in docs for dbus_connection_set_watch_functions().)
+ * A side effect of this is that you need to know there's
+ * a reference held on the connection while invoking
+ * dbus_connection_get_data(), or the connection could be
+ * finalized during dbus_connection_get_data().
+ *
  * @param connection the connection
  * @param slot the slot to get data from
  * @returns the data, or #NULL if not found
@@ -5965,13 +5937,13 @@ dbus_connection_get_data (DBusConnection   *connection,
 
   _dbus_return_val_if_fail (connection != NULL, NULL);
   
-  CONNECTION_LOCK (connection);
+  SLOTS_LOCK (connection);
 
   res = _dbus_data_slot_list_get (&slot_allocator,
                                   &connection->slot_list,
                                   slot);
   
-  CONNECTION_UNLOCK (connection);
+  SLOTS_UNLOCK (connection);
 
   return res;
 }
@@ -6187,6 +6159,53 @@ dbus_connection_get_outgoing_size (DBusConnection *connection)
   return res;
 }
 
+#ifdef DBUS_ENABLE_STATS
+void
+_dbus_connection_get_stats (DBusConnection *connection,
+                            dbus_uint32_t  *in_messages,
+                            dbus_uint32_t  *in_bytes,
+                            dbus_uint32_t  *in_fds,
+                            dbus_uint32_t  *in_peak_bytes,
+                            dbus_uint32_t  *in_peak_fds,
+                            dbus_uint32_t  *out_messages,
+                            dbus_uint32_t  *out_bytes,
+                            dbus_uint32_t  *out_fds,
+                            dbus_uint32_t  *out_peak_bytes,
+                            dbus_uint32_t  *out_peak_fds,
+                            dbus_uint32_t  *link_cache_size)
+{
+  CONNECTION_LOCK (connection);
+
+  if (in_messages != NULL)
+    *in_messages = connection->n_incoming;
+
+  _dbus_transport_get_stats (connection->transport,
+                             in_bytes, in_fds, in_peak_bytes, in_peak_fds);
+
+  if (out_messages != NULL)
+    *out_messages = connection->n_outgoing;
+
+  if (out_bytes != NULL)
+    *out_bytes = _dbus_counter_get_size_value (connection->outgoing_counter);
+
+  if (out_fds != NULL)
+    *out_fds = _dbus_counter_get_unix_fd_value (connection->outgoing_counter);
+
+  if (out_peak_bytes != NULL)
+    *out_peak_bytes = _dbus_counter_get_peak_size_value (connection->outgoing_counter);
+
+  if (out_peak_fds != NULL)
+    *out_peak_fds = _dbus_counter_get_peak_unix_fd_value (connection->outgoing_counter);
+
+  if (link_cache_size != NULL)
+    {
+      *link_cache_size = _dbus_list_get_length (&connection->link_cache) * sizeof (DBusList);
+    }
+
+  CONNECTION_UNLOCK (connection);
+}
+#endif /* DBUS_ENABLE_STATS */
+
 /**
  * Gets the approximate number of uni fds of all messages in the
  * outgoing message queue.