Distinguish between two flavours of mutex
authorSimon McVittie <simon.mcvittie@collabora.co.uk>
Tue, 14 Feb 2012 19:58:56 +0000 (19:58 +0000)
committerSimon McVittie <simon.mcvittie@collabora.co.uk>
Tue, 21 Feb 2012 14:41:25 +0000 (14:41 +0000)
dbus-threads.h warns that recursive pthreads mutexes are not compatible
with our expectations for condition variables. However, the only two
condition variables we actually use only have their corresponding
mutexes locked briefly (and we don't call out to user code from there),
so the mutexes don't need to be recursive anyway. That's just as well,
because it turns out our implementation of recursive mutexes on
pthreads is broken!

The goal here is to be able to distinguish between "cmutexes" (mutexes
compatible with a condition variable) and "rmutexes" (mutexes which
are recursive if possible, to avoid deadlocking if we hold them while
calling user code).

This is complicated by the fact that callers are not guaranteed to have
provided us with both versions of mutexes, so we might have to implement
one by using the other (in particular, DBusRMutex *aims to be*
recursive, it is not *guaranteed to be* recursive).

Bug: https://bugs.freedesktop.org/show_bug.cgi?id=43744
Signed-off-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
Reviewed-by: Thiago Macieira <thiago@kde.org>
dbus/dbus-connection.c
dbus/dbus-dataslot.c
dbus/dbus-dataslot.h
dbus/dbus-internals.h
dbus/dbus-server-protected.h
dbus/dbus-server.c
dbus/dbus-threads-internal.h
dbus/dbus-threads.c

index b8f48ad..74a9007 100644 (file)
 
 #define CONNECTION_LOCK(connection)   do {                                      \
     if (TRACE_LOCKS) { _dbus_verbose ("LOCK\n"); }   \
-    _dbus_mutex_lock ((connection)->mutex);                                      \
+    _dbus_rmutex_lock ((connection)->mutex);                                    \
     TOOK_LOCK_CHECK (connection);                                               \
   } while (0)
 
 #define CONNECTION_UNLOCK(connection) _dbus_connection_unlock (connection)
 
 #define SLOTS_LOCK(connection) do {                     \
-    _dbus_mutex_lock ((connection)->slot_mutex);        \
+    _dbus_rmutex_lock ((connection)->slot_mutex);       \
   } while (0)
 
 #define SLOTS_UNLOCK(connection) do {                   \
-    _dbus_mutex_unlock ((connection)->slot_mutex);      \
+    _dbus_rmutex_unlock ((connection)->slot_mutex);     \
   } while (0)
 
 #define DISPATCH_STATUS_NAME(s)                                            \
@@ -264,11 +264,11 @@ struct DBusConnection
 {
   DBusAtomic refcount; /**< Reference count. */
 
-  DBusMutex *mutex; /**< Lock on the entire DBusConnection */
+  DBusRMutex *mutex; /**< Lock on the entire DBusConnection */
 
-  DBusMutex *dispatch_mutex;     /**< Protects dispatch_acquired */
+  DBusCMutex *dispatch_mutex;     /**< Protects dispatch_acquired */
   DBusCondVar *dispatch_cond;    /**< Notify when dispatch_acquired is available */
-  DBusMutex *io_path_mutex;      /**< Protects io_path_acquired */
+  DBusCMutex *io_path_mutex;      /**< Protects io_path_acquired */
   DBusCondVar *io_path_cond;     /**< Notify when io_path_acquired is available */
   
   DBusList *outgoing_messages; /**< Queue of messages we need to send, send the end of the list first. */
@@ -290,7 +290,7 @@ struct DBusConnection
   
   DBusList *filter_list;        /**< List of filters. */
 
-  DBusMutex *slot_mutex;        /**< Lock on slot_list so overall connection lock need not be taken */
+  DBusRMutex *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. */  
@@ -419,7 +419,7 @@ _dbus_connection_unlock (DBusConnection *connection)
   connection->expired_messages = NULL;
 
   RELEASING_LOCK_CHECK (connection);
-  _dbus_mutex_unlock (connection->mutex);
+  _dbus_rmutex_unlock (connection->mutex);
 
   for (iter = _dbus_list_pop_first_link (&expired_messages);
       iter != NULL;
@@ -467,9 +467,9 @@ _dbus_connection_test_get_locks (DBusConnection *connection,
                                  DBusCondVar   **dispatch_cond_loc,
                                  DBusCondVar   **io_path_cond_loc)
 {
-  *mutex_loc = connection->mutex;
-  *dispatch_mutex_loc = connection->dispatch_mutex;
-  *io_path_mutex_loc = connection->io_path_mutex; 
+  *mutex_loc = (DBusMutex *) connection->mutex;
+  *dispatch_mutex_loc = (DBusMutex *) connection->dispatch_mutex;
+  *io_path_mutex_loc = (DBusMutex *) connection->io_path_mutex;
   *dispatch_cond_loc = connection->dispatch_cond;
   *io_path_cond_loc = connection->io_path_cond;
 }
@@ -1079,7 +1079,7 @@ _dbus_connection_acquire_io_path (DBusConnection *connection,
   CONNECTION_UNLOCK (connection);
   
   _dbus_verbose ("locking io_path_mutex\n");
-  _dbus_mutex_lock (connection->io_path_mutex);
+  _dbus_cmutex_lock (connection->io_path_mutex);
 
   _dbus_verbose ("start connection->io_path_acquired = %d timeout = %d\n",
                  connection->io_path_acquired, timeout_milliseconds);
@@ -1128,7 +1128,7 @@ _dbus_connection_acquire_io_path (DBusConnection *connection,
                  connection->io_path_acquired, we_acquired);
 
   _dbus_verbose ("unlocking io_path_mutex\n");
-  _dbus_mutex_unlock (connection->io_path_mutex);
+  _dbus_cmutex_unlock (connection->io_path_mutex);
 
   CONNECTION_LOCK (connection);
   
@@ -1152,7 +1152,7 @@ _dbus_connection_release_io_path (DBusConnection *connection)
   HAVE_LOCK_CHECK (connection);
   
   _dbus_verbose ("locking io_path_mutex\n");
-  _dbus_mutex_lock (connection->io_path_mutex);
+  _dbus_cmutex_lock (connection->io_path_mutex);
   
   _dbus_assert (connection->io_path_acquired);
 
@@ -1163,7 +1163,7 @@ _dbus_connection_release_io_path (DBusConnection *connection)
   _dbus_condvar_wake_one (connection->io_path_cond);
 
   _dbus_verbose ("unlocking io_path_mutex\n");
-  _dbus_mutex_unlock (connection->io_path_mutex);
+  _dbus_cmutex_unlock (connection->io_path_mutex);
 }
 
 /**
@@ -1292,15 +1292,15 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
   if (connection == NULL)
     goto error;
 
-  _dbus_mutex_new_at_location (&connection->mutex);
+  _dbus_rmutex_new_at_location (&connection->mutex);
   if (connection->mutex == NULL)
     goto error;
 
-  _dbus_mutex_new_at_location (&connection->io_path_mutex);
+  _dbus_cmutex_new_at_location (&connection->io_path_mutex);
   if (connection->io_path_mutex == NULL)
     goto error;
 
-  _dbus_mutex_new_at_location (&connection->dispatch_mutex);
+  _dbus_cmutex_new_at_location (&connection->dispatch_mutex);
   if (connection->dispatch_mutex == NULL)
     goto error;
   
@@ -1312,7 +1312,7 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
   if (connection->io_path_cond == NULL)
     goto error;
 
-  _dbus_mutex_new_at_location (&connection->slot_mutex);
+  _dbus_rmutex_new_at_location (&connection->slot_mutex);
   if (connection->slot_mutex == NULL)
     goto error;
 
@@ -1391,10 +1391,10 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
     {
       _dbus_condvar_free_at_location (&connection->io_path_cond);
       _dbus_condvar_free_at_location (&connection->dispatch_cond);
-      _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_rmutex_free_at_location (&connection->mutex);
+      _dbus_cmutex_free_at_location (&connection->io_path_mutex);
+      _dbus_cmutex_free_at_location (&connection->dispatch_mutex);
+      _dbus_rmutex_free_at_location (&connection->slot_mutex);
       dbus_free (connection);
     }
   if (pending_replies)
@@ -2739,12 +2739,12 @@ _dbus_connection_last_unref (DBusConnection *connection)
   _dbus_condvar_free_at_location (&connection->dispatch_cond);
   _dbus_condvar_free_at_location (&connection->io_path_cond);
 
-  _dbus_mutex_free_at_location (&connection->io_path_mutex);
-  _dbus_mutex_free_at_location (&connection->dispatch_mutex);
+  _dbus_cmutex_free_at_location (&connection->io_path_mutex);
+  _dbus_cmutex_free_at_location (&connection->dispatch_mutex);
 
-  _dbus_mutex_free_at_location (&connection->slot_mutex);
+  _dbus_rmutex_free_at_location (&connection->slot_mutex);
 
-  _dbus_mutex_free_at_location (&connection->mutex);
+  _dbus_rmutex_free_at_location (&connection->mutex);
   
   dbus_free (connection);
 }
@@ -4088,7 +4088,7 @@ _dbus_connection_acquire_dispatch (DBusConnection *connection)
   CONNECTION_UNLOCK (connection);
   
   _dbus_verbose ("locking dispatch_mutex\n");
-  _dbus_mutex_lock (connection->dispatch_mutex);
+  _dbus_cmutex_lock (connection->dispatch_mutex);
 
   while (connection->dispatch_acquired)
     {
@@ -4102,7 +4102,7 @@ _dbus_connection_acquire_dispatch (DBusConnection *connection)
   connection->dispatch_acquired = TRUE;
 
   _dbus_verbose ("unlocking dispatch_mutex\n");
-  _dbus_mutex_unlock (connection->dispatch_mutex);
+  _dbus_cmutex_unlock (connection->dispatch_mutex);
   
   CONNECTION_LOCK (connection);
   _dbus_connection_unref_unlocked (connection);
@@ -4121,7 +4121,7 @@ _dbus_connection_release_dispatch (DBusConnection *connection)
   HAVE_LOCK_CHECK (connection);
   
   _dbus_verbose ("locking dispatch_mutex\n");
-  _dbus_mutex_lock (connection->dispatch_mutex);
+  _dbus_cmutex_lock (connection->dispatch_mutex);
   
   _dbus_assert (connection->dispatch_acquired);
 
@@ -4129,7 +4129,7 @@ _dbus_connection_release_dispatch (DBusConnection *connection)
   _dbus_condvar_wake_one (connection->dispatch_cond);
 
   _dbus_verbose ("unlocking dispatch_mutex\n");
-  _dbus_mutex_unlock (connection->dispatch_mutex);
+  _dbus_cmutex_unlock (connection->dispatch_mutex);
 }
 
 static void
index fd25c41..0369612 100644 (file)
@@ -67,12 +67,12 @@ _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator)
  */
 dbus_bool_t
 _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator,
-                                 DBusMutex             **mutex_loc,
+                                 DBusRMutex            **mutex_loc,
                                  dbus_int32_t          *slot_id_p)
 {
   dbus_int32_t slot;
 
-  _dbus_mutex_lock (*mutex_loc);
+  _dbus_rmutex_lock (*mutex_loc);
 
   if (allocator->n_allocated_slots == 0)
     {
@@ -146,7 +146,7 @@ _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator,
                  slot, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
   
  out:
-  _dbus_mutex_unlock (*(allocator->lock_loc));
+  _dbus_rmutex_unlock (*(allocator->lock_loc));
   return slot >= 0;
 }
 
@@ -165,7 +165,7 @@ void
 _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
                                 dbus_int32_t          *slot_id_p)
 {
-  _dbus_mutex_lock (*(allocator->lock_loc));
+  _dbus_rmutex_lock (*(allocator->lock_loc));
   
   _dbus_assert (*slot_id_p < allocator->n_allocated_slots);
   _dbus_assert (allocator->allocated_slots[*slot_id_p].slot_id == *slot_id_p);
@@ -175,7 +175,7 @@ _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
 
   if (allocator->allocated_slots[*slot_id_p].refcount > 0)
     {
-      _dbus_mutex_unlock (*(allocator->lock_loc));
+      _dbus_rmutex_unlock (*(allocator->lock_loc));
       return;
     }
 
@@ -190,18 +190,18 @@ _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
   
   if (allocator->n_used_slots == 0)
     {
-      DBusMutex **mutex_loc = allocator->lock_loc;
+      DBusRMutex **mutex_loc = allocator->lock_loc;
       
       dbus_free (allocator->allocated_slots);
       allocator->allocated_slots = NULL;
       allocator->n_allocated_slots = 0;
       allocator->lock_loc = NULL;
 
-      _dbus_mutex_unlock (*mutex_loc);
+      _dbus_rmutex_unlock (*mutex_loc);
     }
   else
     {
-      _dbus_mutex_unlock (*(allocator->lock_loc));
+      _dbus_rmutex_unlock (*(allocator->lock_loc));
     }
 }
 
@@ -247,10 +247,10 @@ _dbus_data_slot_list_set  (DBusDataSlotAllocator *allocator,
    * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
    * are disabled, since then the asserts are empty.
    */
-  _dbus_mutex_lock (*(allocator->lock_loc));
+  _dbus_rmutex_lock (*(allocator->lock_loc));
   _dbus_assert (slot < allocator->n_allocated_slots);
   _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
-  _dbus_mutex_unlock (*(allocator->lock_loc));
+  _dbus_rmutex_unlock (*(allocator->lock_loc));
 #endif
   
   if (slot >= list->n_slots)
@@ -304,11 +304,11 @@ _dbus_data_slot_list_get  (DBusDataSlotAllocator *allocator,
    * be e.g. realloc()ing allocated_slots. We avoid doing this if asserts
    * are disabled, since then the asserts are empty.
    */
-  _dbus_mutex_lock (*(allocator->lock_loc));
+  _dbus_rmutex_lock (*(allocator->lock_loc));
   _dbus_assert (slot >= 0);
   _dbus_assert (slot < allocator->n_allocated_slots);
   _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
-  _dbus_mutex_unlock (*(allocator->lock_loc));
+  _dbus_rmutex_unlock (*(allocator->lock_loc));
 #endif
 
   if (slot >= list->n_slots)
@@ -384,14 +384,14 @@ _dbus_data_slot_test (void)
   int i;
   DBusFreeFunction old_free_func;
   void *old_data;
-  DBusMutex *mutex;
+  DBusRMutex *mutex;
   
   if (!_dbus_data_slot_allocator_init (&allocator))
     _dbus_assert_not_reached ("no memory for allocator");
 
   _dbus_data_slot_list_init (&list);
 
-  _dbus_mutex_new_at_location (&mutex);
+  _dbus_rmutex_new_at_location (&mutex);
   if (mutex == NULL)
     _dbus_assert_not_reached ("failed to alloc mutex");
   
@@ -471,7 +471,7 @@ _dbus_data_slot_test (void)
       ++i;
     }
 
-  _dbus_mutex_free_at_location (&mutex);
+  _dbus_rmutex_free_at_location (&mutex);
   
   return TRUE;
 }
index 2e706f7..3d9d5ed 100644 (file)
@@ -57,7 +57,7 @@ struct DBusDataSlotAllocator
   DBusAllocatedSlot *allocated_slots; /**< Allocated slots */
   int  n_allocated_slots; /**< number of slots malloc'd */
   int  n_used_slots;      /**< number of slots used */
-  DBusMutex **lock_loc;   /**< location of thread lock */
+  DBusRMutex **lock_loc;  /**< location of thread lock */
 };
 
 /**
@@ -72,7 +72,7 @@ struct DBusDataSlotList
 
 dbus_bool_t _dbus_data_slot_allocator_init  (DBusDataSlotAllocator  *allocator);
 dbus_bool_t _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator  *allocator,
-                                             DBusMutex              **mutex_loc,
+                                             DBusRMutex             **mutex_loc,
                                              int                    *slot_id_p);
 void        _dbus_data_slot_allocator_free  (DBusDataSlotAllocator  *allocator,
                                              int                    *slot_id_p);
index 162a222..8036a2b 100644 (file)
@@ -304,10 +304,10 @@ extern int _dbus_current_generation;
 
 /* Thread initializers */
 #define _DBUS_LOCK_NAME(name)           _dbus_lock_##name
-#define _DBUS_DECLARE_GLOBAL_LOCK(name) extern DBusMutex  *_dbus_lock_##name
-#define _DBUS_DEFINE_GLOBAL_LOCK(name)  DBusMutex         *_dbus_lock_##name  
-#define _DBUS_LOCK(name)                _dbus_mutex_lock   (_dbus_lock_##name)
-#define _DBUS_UNLOCK(name)              _dbus_mutex_unlock (_dbus_lock_##name)
+#define _DBUS_DECLARE_GLOBAL_LOCK(name) extern DBusRMutex *_dbus_lock_##name
+#define _DBUS_DEFINE_GLOBAL_LOCK(name)  DBusRMutex        *_dbus_lock_##name
+#define _DBUS_LOCK(name)                _dbus_rmutex_lock   (_dbus_lock_##name)
+#define _DBUS_UNLOCK(name)              _dbus_rmutex_unlock (_dbus_lock_##name)
 
 /* 1-5 */
 _DBUS_DECLARE_GLOBAL_LOCK (list);
index df306f7..dd5234b 100644 (file)
@@ -57,7 +57,7 @@ struct DBusServer
 {
   DBusAtomic refcount;                        /**< Reference count. */
   const DBusServerVTable *vtable;             /**< Virtual methods for this instance. */
-  DBusMutex *mutex;                           /**< Lock on the server object */
+  DBusRMutex *mutex;                          /**< Lock on the server object */
 
   DBusGUID guid;                              /**< Globally unique ID of server */
 
@@ -161,14 +161,14 @@ void _dbus_server_trace_ref (DBusServer *server,
 
 #define SERVER_LOCK(server)   do {                                              \
     if (TRACE_LOCKS) { _dbus_verbose ("LOCK\n"); }   \
-    _dbus_mutex_lock ((server)->mutex);                                          \
+    _dbus_rmutex_lock ((server)->mutex);                                        \
     TOOK_LOCK_CHECK (server);                                                   \
   } while (0)
 
 #define SERVER_UNLOCK(server) do {                                                      \
     if (TRACE_LOCKS) { _dbus_verbose ("UNLOCK\n");  }        \
     RELEASING_LOCK_CHECK (server);                                                      \
-    _dbus_mutex_unlock ((server)->mutex);                                                \
+    _dbus_rmutex_unlock ((server)->mutex);                                              \
   } while (0)
 
 DBUS_END_DECLS
index 157557c..b62c2b4 100644 (file)
@@ -142,7 +142,7 @@ _dbus_server_init_base (DBusServer             *server,
   if (server->address == NULL)
     goto failed;
   
-  _dbus_mutex_new_at_location (&server->mutex);
+  _dbus_rmutex_new_at_location (&server->mutex);
   if (server->mutex == NULL)
     goto failed;
   
@@ -161,7 +161,7 @@ _dbus_server_init_base (DBusServer             *server,
   return TRUE;
 
  failed:
-  _dbus_mutex_free_at_location (&server->mutex);
+  _dbus_rmutex_free_at_location (&server->mutex);
   server->mutex = NULL;
   if (server->watches)
     {
@@ -208,7 +208,7 @@ _dbus_server_finalize_base (DBusServer *server)
   _dbus_watch_list_free (server->watches);
   _dbus_timeout_list_free (server->timeouts);
 
-  _dbus_mutex_free_at_location (&server->mutex);
+  _dbus_rmutex_free_at_location (&server->mutex);
   
   dbus_free (server->address);
 
@@ -1093,7 +1093,7 @@ dbus_bool_t
 dbus_server_allocate_data_slot (dbus_int32_t *slot_p)
 {
   return _dbus_data_slot_allocator_alloc (&slot_allocator,
-                                          (DBusMutex **)&_DBUS_LOCK_NAME (server_slots),
+                                          (DBusRMutex **)&_DBUS_LOCK_NAME (server_slots),
                                           slot_p);
 }
 
index 68aa20a..2561136 100644 (file)
 #include <dbus/dbus-types.h>
 #include <dbus/dbus-threads.h>
 
+/**
+ * @addtogroup DBusThreadsInternals
+ * @{
+ */
+
+/**
+ * A mutex which is recursive if possible, else non-recursive.
+ * This is typically recursive, but that cannot be relied upon.
+ */
+typedef struct DBusRMutex DBusRMutex;
+
+/**
+ * A mutex suitable for use with condition variables.
+ * This is typically non-recursive.
+ */
+typedef struct DBusCMutex DBusCMutex;
+
+/** @} */
+
 DBUS_BEGIN_DECLS
 
-void         _dbus_mutex_lock                (DBusMutex         *mutex);
-void         _dbus_mutex_unlock              (DBusMutex         *mutex);
-void         _dbus_mutex_new_at_location     (DBusMutex        **location_p);
-void         _dbus_mutex_free_at_location    (DBusMutex        **location_p);
+void         _dbus_rmutex_lock               (DBusRMutex       *mutex);
+void         _dbus_rmutex_unlock             (DBusRMutex       *mutex);
+void         _dbus_rmutex_new_at_location    (DBusRMutex      **location_p);
+void         _dbus_rmutex_free_at_location   (DBusRMutex      **location_p);
+
+void         _dbus_cmutex_lock               (DBusCMutex       *mutex);
+void         _dbus_cmutex_unlock             (DBusCMutex       *mutex);
+void         _dbus_cmutex_new_at_location    (DBusCMutex      **location_p);
+void         _dbus_cmutex_free_at_location   (DBusCMutex      **location_p);
 
 DBusCondVar* _dbus_condvar_new               (void);
 void         _dbus_condvar_free              (DBusCondVar       *cond);
 void         _dbus_condvar_wait              (DBusCondVar       *cond,
-                                              DBusMutex         *mutex);
+                                              DBusCMutex        *mutex);
 dbus_bool_t  _dbus_condvar_wait_timeout      (DBusCondVar       *cond,
-                                              DBusMutex         *mutex,
+                                              DBusCMutex        *mutex,
                                               int                timeout_milliseconds);
 void         _dbus_condvar_wake_one          (DBusCondVar       *cond);
 void         _dbus_condvar_wake_all          (DBusCondVar       *cond);
index 5eda6f1..eff3054 100644 (file)
@@ -38,7 +38,8 @@ static DBusThreadFunctions thread_functions =
 
 static int thread_init_generation = 0;
  
-static DBusList *uninitialized_mutex_list = NULL;
+static DBusList *uninitialized_rmutex_list = NULL;
+static DBusList *uninitialized_cmutex_list = NULL;
 static DBusList *uninitialized_condvar_list = NULL;
 
 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
@@ -47,12 +48,19 @@ static DBusList *uninitialized_condvar_list = NULL;
 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
 #define _DBUS_DUMMY_CONDVAR ((DBusCondVar*)0xABCDEF2)
 
-static void _dbus_mutex_free (DBusMutex *mutex);
+static void _dbus_mutex_free (DBusMutex *mutex, DBusMutexFreeFunction dtor);
+static void _dbus_mutex_new_at_location (DBusMutex            **location_p,
+                                         DBusMutexNewFunction   ctor,
+                                         DBusMutexFreeFunction  dtor,
+                                         DBusList             **uninitialized);
+static void _dbus_mutex_free_at_location (DBusMutex            **location_p,
+                                          DBusMutexFreeFunction  dtor,
+                                          DBusList             **uninitialized);
 
 /**
  * @defgroup DBusThreadsInternals Thread functions
  * @ingroup  DBusInternals
- * @brief _dbus_mutex_lock(), etc.
+ * @brief _dbus_rmutex_lock(), etc.
  *
  * Functions and macros related to threads and thread locks.
  *
@@ -60,9 +68,11 @@ static void _dbus_mutex_free (DBusMutex *mutex);
  */
 
 static DBusMutex *
-_dbus_mutex_new (void)
+_dbus_mutex_new (DBusMutexNewFunction ctor)
 {
-  if (thread_functions.recursive_mutex_new)
+  if (ctor)
+    return ctor ();
+  else if (thread_functions.recursive_mutex_new)
     return (* thread_functions.recursive_mutex_new) ();
   else if (thread_functions.mutex_new)
     return (* thread_functions.mutex_new) ();
@@ -76,6 +86,33 @@ _dbus_mutex_new (void)
  * May return #NULL even if threads are initialized, indicating
  * out-of-memory.
  *
+ * If possible, the mutex returned by this function is recursive, to
+ * avoid deadlocks. However, that cannot be relied on.
+ *
+ * The extra level of indirection given by allocating a pointer
+ * to point to the mutex location allows the threading
+ * module to swap out dummy mutexes for real a real mutex so libraries
+ * can initialize threads even after the D-Bus API has been used.
+ *
+ * @param location_p the location of the new mutex, can return #NULL on OOM
+ */
+void
+_dbus_rmutex_new_at_location (DBusRMutex **location_p)
+{
+  _dbus_mutex_new_at_location ((DBusMutex **) location_p,
+                               thread_functions.recursive_mutex_new,
+                               thread_functions.recursive_mutex_free,
+                               &uninitialized_rmutex_list);
+}
+
+/**
+ * Creates a new mutex using the function supplied to dbus_threads_init(),
+ * or creates a no-op mutex if threads are not initialized.
+ * May return #NULL even if threads are initialized, indicating
+ * out-of-memory.
+ *
+ * The returned mutex is suitable for use with condition variables.
+ *
  * The extra level of indirection given by allocating a pointer
  * to point to the mutex location allows the threading
  * module to swap out dummy mutexes for real a real mutex so libraries
@@ -84,65 +121,120 @@ _dbus_mutex_new (void)
  * @param location_p the location of the new mutex, can return #NULL on OOM
  */
 void
-_dbus_mutex_new_at_location (DBusMutex **location_p)
+_dbus_cmutex_new_at_location (DBusCMutex **location_p)
+{
+  _dbus_mutex_new_at_location ((DBusMutex **) location_p,
+                               thread_functions.mutex_new,
+                               thread_functions.mutex_free,
+                               &uninitialized_cmutex_list);
+}
+
+static void
+_dbus_mutex_new_at_location (DBusMutex            **location_p,
+                             DBusMutexNewFunction   ctor,
+                             DBusMutexFreeFunction  dtor,
+                             DBusList             **uninitialized)
 {
   _dbus_assert (location_p != NULL);
 
-  *location_p = _dbus_mutex_new();
+  *location_p = _dbus_mutex_new (ctor);
 
   if (thread_init_generation != _dbus_current_generation && *location_p)
     {
-      if (!_dbus_list_append (&uninitialized_mutex_list, location_p))
+      if (!_dbus_list_append (uninitialized, location_p))
         {
-         _dbus_mutex_free (*location_p);
+         _dbus_mutex_free (*location_p, dtor);
          *location_p = NULL;
        }
     }
 }
 
 static void
-_dbus_mutex_free (DBusMutex *mutex)
+_dbus_mutex_free (DBusMutex             *mutex,
+                  DBusMutexFreeFunction  dtor)
 {
   if (mutex)
     {
-      if (mutex && thread_functions.recursive_mutex_free)
+      if (dtor)
+        dtor (mutex);
+      else if (mutex && thread_functions.recursive_mutex_free)
         (* thread_functions.recursive_mutex_free) (mutex);
       else if (mutex && thread_functions.mutex_free)
         (* thread_functions.mutex_free) (mutex);
     }
 }
 
-/**
- * Frees a mutex and removes it from the 
- * uninitialized_mutex_list;
- * does nothing if passed a #NULL pointer.
- */
-void
-_dbus_mutex_free_at_location (DBusMutex **location_p)
+static void
+_dbus_mutex_free_at_location (DBusMutex            **location_p,
+                              DBusMutexFreeFunction  dtor,
+                              DBusList             **uninitialized)
 {
   if (location_p)
     {
       if (thread_init_generation != _dbus_current_generation)
-        _dbus_list_remove (&uninitialized_mutex_list, location_p);
+        _dbus_list_remove (uninitialized, location_p);
 
-      _dbus_mutex_free (*location_p);
+      _dbus_mutex_free (*location_p, dtor);
     }
 }
 
 /**
+ * Frees a DBusRMutex and removes it from the
+ * uninitialized mutex list;
+ * does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_rmutex_free_at_location (DBusRMutex **location_p)
+{
+  _dbus_mutex_free_at_location ((DBusMutex **) location_p,
+                                thread_functions.recursive_mutex_free,
+                                &uninitialized_rmutex_list);
+}
+
+/**
+ * Frees a DBusCMutex and removes it from the
+ * uninitialized mutex list;
+ * does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_cmutex_free_at_location (DBusCMutex **location_p)
+{
+  _dbus_mutex_free_at_location ((DBusMutex **) location_p,
+                                thread_functions.mutex_free,
+                                &uninitialized_cmutex_list);
+}
+
+/**
  * Locks a mutex. Does nothing if passed a #NULL pointer.
  * Locks may be recursive if threading implementation initialized
  * recursive locks.
  */
 void
-_dbus_mutex_lock (DBusMutex *mutex)
+_dbus_rmutex_lock (DBusRMutex *mutex)
 {
-  if (mutex) 
+  if (mutex)
     {
       if (thread_functions.recursive_mutex_lock)
-        (* thread_functions.recursive_mutex_lock) (mutex);
+        (* thread_functions.recursive_mutex_lock) ((DBusMutex *) mutex);
       else if (thread_functions.mutex_lock)
-        (* thread_functions.mutex_lock) (mutex);
+        (* thread_functions.mutex_lock) ((DBusMutex *) mutex);
+    }
+}
+
+/**
+ * Locks a mutex. Does nothing if passed a #NULL pointer.
+ * Locks may be recursive if threading implementation initialized
+ * recursive locks.
+ */
+void
+_dbus_cmutex_lock (DBusCMutex *mutex)
+{
+  if (mutex)
+    {
+      if (thread_functions.mutex_lock)
+        (* thread_functions.mutex_lock) ((DBusMutex *) mutex);
+      else if (thread_functions.recursive_mutex_lock)
+        (* thread_functions.recursive_mutex_lock) ((DBusMutex *) mutex);
     }
 }
 
@@ -152,14 +244,31 @@ _dbus_mutex_lock (DBusMutex *mutex)
  * @returns #TRUE on success
  */
 void
-_dbus_mutex_unlock (DBusMutex *mutex)
+_dbus_rmutex_unlock (DBusRMutex *mutex)
 {
   if (mutex)
     {
       if (thread_functions.recursive_mutex_unlock)
-        (* thread_functions.recursive_mutex_unlock) (mutex);
+        (* thread_functions.recursive_mutex_unlock) ((DBusMutex *) mutex);
       else if (thread_functions.mutex_unlock)
-        (* thread_functions.mutex_unlock) (mutex);
+        (* thread_functions.mutex_unlock) ((DBusMutex *) mutex);
+    }
+}
+
+/**
+ * Unlocks a mutex. Does nothing if passed a #NULL pointer.
+ *
+ * @returns #TRUE on success
+ */
+void
+_dbus_cmutex_unlock (DBusCMutex *mutex)
+{
+  if (mutex)
+    {
+      if (thread_functions.mutex_unlock)
+        (* thread_functions.mutex_unlock) ((DBusMutex *) mutex);
+      else if (thread_functions.recursive_mutex_unlock)
+        (* thread_functions.recursive_mutex_unlock) ((DBusMutex *) mutex);
     }
 }
 
@@ -243,10 +352,10 @@ _dbus_condvar_free_at_location (DBusCondVar **location_p)
  */
 void
 _dbus_condvar_wait (DBusCondVar *cond,
-                    DBusMutex   *mutex)
+                    DBusCMutex  *mutex)
 {
   if (cond && mutex && thread_functions.condvar_wait)
-    (* thread_functions.condvar_wait) (cond, mutex);
+    (* thread_functions.condvar_wait) (cond, (DBusMutex *) mutex);
 }
 
 /**
@@ -262,11 +371,13 @@ _dbus_condvar_wait (DBusCondVar *cond,
  */
 dbus_bool_t
 _dbus_condvar_wait_timeout (DBusCondVar               *cond,
-                            DBusMutex                 *mutex,
+                            DBusCMutex                *mutex,
                             int                        timeout_milliseconds)
 {
   if (cond && mutex && thread_functions.condvar_wait)
-    return (* thread_functions.condvar_wait_timeout) (cond, mutex, timeout_milliseconds);
+    return (* thread_functions.condvar_wait_timeout) (cond,
+                                                      (DBusMutex *) mutex,
+                                                      timeout_milliseconds);
   else
     return TRUE;
 }
@@ -304,7 +415,7 @@ shutdown_global_locks (void *data)
   i = 0;
   while (i < _DBUS_N_GLOBAL_LOCKS)
     {
-      _dbus_mutex_free (*(locks[i]));
+      _dbus_mutex_free (*(locks[i]), thread_functions.recursive_mutex_free);
       *(locks[i]) = NULL;
       ++i;
     }
@@ -315,7 +426,8 @@ shutdown_global_locks (void *data)
 static void
 shutdown_uninitialized_locks (void *data)
 {
-  _dbus_list_clear (&uninitialized_mutex_list);
+  _dbus_list_clear (&uninitialized_rmutex_list);
+  _dbus_list_clear (&uninitialized_cmutex_list);
   _dbus_list_clear (&uninitialized_condvar_list);
 }
 
@@ -326,19 +438,34 @@ init_uninitialized_locks (void)
 
   _dbus_assert (thread_init_generation != _dbus_current_generation);
 
-  link = uninitialized_mutex_list;
+  link = uninitialized_rmutex_list;
+  while (link != NULL)
+    {
+      DBusMutex **mp;
+
+      mp = link->data;
+      _dbus_assert (*mp == _DBUS_DUMMY_MUTEX);
+
+      *mp = _dbus_mutex_new (thread_functions.recursive_mutex_new);
+      if (*mp == NULL)
+        goto fail_mutex;
+
+      link = _dbus_list_get_next_link (&uninitialized_rmutex_list, link);
+    }
+
+  link = uninitialized_cmutex_list;
   while (link != NULL)
     {
       DBusMutex **mp;
 
-      mp = (DBusMutex **)link->data;
+      mp = link->data;
       _dbus_assert (*mp == _DBUS_DUMMY_MUTEX);
 
-      *mp = _dbus_mutex_new ();
+      *mp = _dbus_mutex_new (thread_functions.mutex_new);
       if (*mp == NULL)
         goto fail_mutex;
 
-      link = _dbus_list_get_next_link (&uninitialized_mutex_list, link);
+      link = _dbus_list_get_next_link (&uninitialized_cmutex_list, link);
     }
 
   link = uninitialized_condvar_list;
@@ -356,7 +483,8 @@ init_uninitialized_locks (void)
       link = _dbus_list_get_next_link (&uninitialized_condvar_list, link);
     }
 
-  _dbus_list_clear (&uninitialized_mutex_list);
+  _dbus_list_clear (&uninitialized_rmutex_list);
+  _dbus_list_clear (&uninitialized_cmutex_list);
   _dbus_list_clear (&uninitialized_condvar_list);
 
   if (!_dbus_register_shutdown_func (shutdown_uninitialized_locks,
@@ -384,21 +512,38 @@ init_uninitialized_locks (void)
     }
 
  fail_mutex:
-  link = uninitialized_mutex_list;
+  link = uninitialized_rmutex_list;
   while (link != NULL)
     {
       DBusMutex **mp;
 
-      mp = (DBusMutex **)link->data;
+      mp = link->data;
 
       if (*mp != _DBUS_DUMMY_MUTEX)
-        _dbus_mutex_free (*mp);
+        _dbus_mutex_free (*mp, thread_functions.recursive_mutex_free);
       else
         break;
 
       *mp = _DBUS_DUMMY_MUTEX;
 
-      link = _dbus_list_get_next_link (&uninitialized_mutex_list, link);
+      link = _dbus_list_get_next_link (&uninitialized_rmutex_list, link);
+    }
+
+  link = uninitialized_cmutex_list;
+  while (link != NULL)
+    {
+      DBusMutex **mp;
+
+      mp = link->data;
+
+      if (*mp != _DBUS_DUMMY_MUTEX)
+        _dbus_mutex_free (*mp, thread_functions.mutex_free);
+      else
+        break;
+
+      *mp = _DBUS_DUMMY_MUTEX;
+
+      link = _dbus_list_get_next_link (&uninitialized_cmutex_list, link);
     }
 
   return FALSE;
@@ -408,9 +553,8 @@ static dbus_bool_t
 init_locks (void)
 {
   int i;
-  DBusMutex ***dynamic_global_locks;
-  
-  DBusMutex **global_locks[] = {
+  DBusRMutex ***dynamic_global_locks;
+  DBusRMutex **global_locks[] = {
 #define LOCK_ADDR(name) (& _dbus_lock_##name)
     LOCK_ADDR (win_fds),
     LOCK_ADDR (sid_atom_cache),
@@ -437,14 +581,14 @@ init_locks (void)
 
   i = 0;
   
-  dynamic_global_locks = dbus_new (DBusMutex**, _DBUS_N_GLOBAL_LOCKS);
+  dynamic_global_locks = dbus_new (DBusRMutex**, _DBUS_N_GLOBAL_LOCKS);
   if (dynamic_global_locks == NULL)
     goto failed;
   
   while (i < _DBUS_N_ELEMENTS (global_locks))
     {
-      *global_locks[i] = _dbus_mutex_new ();
-      
+      *global_locks[i] = (DBusRMutex *) _dbus_mutex_new (thread_functions.recursive_mutex_new);
+
       if (*global_locks[i] == NULL)
         goto failed;
 
@@ -467,7 +611,8 @@ init_locks (void)
                                      
   for (i = i - 1; i >= 0; i--)
     {
-      _dbus_mutex_free (*global_locks[i]);
+      _dbus_mutex_free ((DBusMutex *) *global_locks[i],
+                        thread_functions.recursive_mutex_free);
       *global_locks[i] = NULL;
     }
   return FALSE;