* dbus/dbus-threads.c: Add static DBusList *uninitialized_mutex_list and
authorJohn (J5) Palmieri <johnp@redhat.com>
Wed, 16 Aug 2006 22:30:15 +0000 (22:30 +0000)
committerJohn (J5) Palmieri <johnp@redhat.com>
Wed, 16 Aug 2006 22:30:15 +0000 (22:30 +0000)
        static DBusList *uninitialized_condvar_list to support new late
        initialization threading model.  In this model threads can be initialized
        even after the D-Bus API has been used but still needs to be initialized
        before the second thread has been started.  Mutexes and condvar addresses
        are stored in the two static lists and are replaced with actuall locks
        when threads are initalized.
        (_dbus_mutex_new_at_location): New method for creating a mutex and placing
        the location into the static list
        (_dbus_mutex_free_at_location): New method for removing a mutex location
        from the static list and freeing the mutex
        (_dbus_condvar_new_at_location): New method for creating a conditional
        variable and placing the location into the static list
        (_dbus_condvar_free_at_location): New method for removing a conditional
        variable location from the static list and freeing the conditional variable
        (init_uninitialized_locks): Atomic method which goes through the static
        lists of mutex and condvar location and updates them with actuall locks
        (init_global_locks): changed to init_locks

        * dbus/dbus-connection.c:
        (_dbus_connection_test_get_locks): New method for tests to check connections
        (_dbus_connection_new_for_transport): Use the new at_location mutex and
        condvar API
        (dbus_connection_allocate_data_slot): Pass in the global lock address
        to _dbus_data_slot_allocator_alloc

        * dbus/dbus-dataslot.c:
        (_dbus_data_slot_allocator_alloc): Use the address of the mutex
        instead of the mutex itself

        * dbus/dbus-message.c:
        (dbus_message_allocate_data_slot): Pass in the global lock address
        to _dbus_data_slot_allocator_alloc

        * dbus/dbus-pending-call.c:
        (dbus_pending_call_allocate_data_slot): Pass in the global lock address
        to _dbus_data_slot_allocator_alloc

        * dbus/dbus-server.c:
        (_dbus_server_init_base): Use the new at_location mutex API
        (dbus_server_allocate_data_slot): Pass in the global lock address
        to _dbus_data_slot_allocator_alloc

        * test/name-test/test-threads-init.c: New test case for late thread
        initialization

12 files changed:
ChangeLog
dbus/dbus-connection.c
dbus/dbus-dataslot.c
dbus/dbus-dataslot.h
dbus/dbus-message.c
dbus/dbus-pending-call.c
dbus/dbus-server.c
dbus/dbus-threads-internal.h
dbus/dbus-threads.c
test/name-test/Makefile.am
test/name-test/run-test.sh
test/name-test/test-threads-init.c [new file with mode: 0644]

index 14f89ed..a394d5c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,45 @@
+2006-08-16  John (J5) Palmieri  <johnp@redhat.com>
+
+       * dbus/dbus-threads.c: Add static DBusList *uninitialized_mutex_list and
+       static DBusList *uninitialized_condvar_list to support new late 
+       initialization threading model.  In this model threads can be initialized
+       even after the D-Bus API has been used but still needs to be initialized 
+       before the second thread has been started.  Mutexes and condvar addresses
+       are stored in the two static lists and are replaced with actuall locks
+       when threads are initalized.
+       (_dbus_mutex_new_at_location): New method for creating a mutex and placing
+       the location into the static list
+       (_dbus_mutex_free_at_location): New method for removing a mutex location 
+       from the static list and freeing the mutex
+       (_dbus_condvar_new_at_location): New method for creating a conditional 
+       variable and placing the location into the static list
+       (_dbus_condvar_free_at_location): New method for removing a conditional
+       variable location from the static list and freeing the conditional variable 
+
+       * dbus/dbus-connection.c:
+       (_dbus_connection_test_get_locks): New method for tests to check connections
+       (_dbus_connection_new_for_transport): Use the new at_location mutex and
+       condvar API
+       (dbus_connection_allocate_data_slot): Pass in the global lock address
+       to _dbus_data_slot_allocator_alloc
+
+       * dbus/dbus-dataslot.c:
+       (_dbus_data_slot_allocator_alloc): Use the address of the mutex
+       instead of the mutex itself
+
+       * dbus/dbus-message.c:
+       (dbus_message_allocate_data_slot): Pass in the global lock address
+       to _dbus_data_slot_allocator_alloc
+
+       * dbus/dbus-pending-call.c:
+       (dbus_pending_call_allocate_data_slot): Pass in the global lock address
+       to _dbus_data_slot_allocator_alloc
+
+       * dbus/dbus-server.c:
+       (_dbus_server_init_base): Use the new at_location mutex API
+       (dbus_server_allocate_data_slot): Pass in the global lock address
+       to _dbus_data_slot_allocator_alloc
+
 2006-08-14  John (J5) Palmieri  <johnp@redhat.com>
 
        * dbus/dbus-dataslot.c (_dbus_data_slot_allocator_alloc):
index 2e94206..2772099 100644 (file)
@@ -347,6 +347,33 @@ _dbus_connection_queue_received_message (DBusConnection *connection,
 
   return TRUE;
 }
+
+/**
+ * Gets the locks so we can examine them
+ *
+ * @param connection the connection.
+ * @param mutex_loc return for the location of the main mutex pointer
+ * @param dispatch_mutex_loc return location of the dispatch mutex pointer
+ * @param io_path_mutex_loc return location of the io_path mutex pointer
+ * @param dispatch_cond_loc return location of the dispatch conditional 
+ *        variable pointer
+ * @param io_path_cond_loc return location of the io_path conditional 
+ *        variable pointer
+ */ 
+void 
+_dbus_connection_test_get_locks (DBusConnection *conn,
+                                 DBusMutex **mutex_loc,
+                                 DBusMutex **dispatch_mutex_loc,
+                                 DBusMutex **io_path_mutex_loc,
+                                 DBusCondVar **dispatch_cond_loc,
+                                 DBusCondVar **io_path_cond_loc)
+{
+  *mutex_loc = conn->mutex;
+  *dispatch_mutex_loc = conn->dispatch_mutex;
+  *io_path_mutex_loc = conn->io_path_mutex; 
+  *dispatch_cond_loc = conn->dispatch_cond;
+  *io_path_cond_loc = conn->io_path_cond;
+}
 #endif
 
 /**
@@ -936,7 +963,8 @@ _dbus_connection_acquire_io_path (DBusConnection *connection,
           while (connection->io_path_acquired)
             {
               _dbus_verbose ("%s waiting for IO path to be acquirable\n", _DBUS_FUNCTION_NAME);
-              _dbus_condvar_wait (connection->io_path_cond, connection->io_path_mutex);
+              _dbus_condvar_wait (connection->io_path_cond, 
+                                  connection->io_path_mutex);
             }
         }
     }
@@ -1060,11 +1088,6 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
   DBusWatchList *watch_list;
   DBusTimeoutList *timeout_list;
   DBusHashTable *pending_replies;
-  DBusMutex *mutex;
-  DBusMutex *io_path_mutex;
-  DBusMutex *dispatch_mutex;
-  DBusCondVar *dispatch_cond;
-  DBusCondVar *io_path_cond;
   DBusList *disconnect_link;
   DBusMessage *disconnect_message;
   DBusCounter *outgoing_counter;
@@ -1074,11 +1097,6 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
   connection = NULL;
   pending_replies = NULL;
   timeout_list = NULL;
-  mutex = NULL;
-  io_path_mutex = NULL;
-  dispatch_mutex = NULL;
-  dispatch_cond = NULL;
-  io_path_cond = NULL;
   disconnect_link = NULL;
   disconnect_message = NULL;
   outgoing_counter = NULL;
@@ -1103,24 +1121,24 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
   if (connection == NULL)
     goto error;
 
-  mutex = _dbus_mutex_new ();
-  if (mutex == NULL)
+  _dbus_mutex_new_at_location (&connection->mutex);
+  if (connection->mutex == NULL)
     goto error;
 
-  io_path_mutex = _dbus_mutex_new ();
-  if (io_path_mutex == NULL)
+  _dbus_mutex_new_at_location (&connection->io_path_mutex);
+  if (connection->io_path_mutex == NULL)
     goto error;
 
-  dispatch_mutex = _dbus_mutex_new ();
-  if (dispatch_mutex == NULL)
+  _dbus_mutex_new_at_location (&connection->dispatch_mutex);
+  if (connection->dispatch_mutex == NULL)
     goto error;
   
-  dispatch_cond = _dbus_condvar_new ();
-  if (dispatch_cond == NULL)
+  _dbus_condvar_new_at_location (&connection->dispatch_cond);
+  if (connection->dispatch_cond == NULL)
     goto error;
   
-  io_path_cond = _dbus_condvar_new ();
-  if (io_path_cond == NULL)
+  _dbus_condvar_new_at_location (&connection->io_path_cond);
+  if (connection->io_path_cond == NULL)
     goto error;
 
   disconnect_message = dbus_message_new_signal (DBUS_PATH_LOCAL,
@@ -1146,11 +1164,6 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
     _dbus_disable_sigpipe ();
   
   connection->refcount.value = 1;
-  connection->mutex = mutex;
-  connection->dispatch_cond = dispatch_cond;
-  connection->dispatch_mutex = dispatch_mutex;
-  connection->io_path_cond = io_path_cond;
-  connection->io_path_mutex = io_path_mutex;
   connection->transport = transport;
   connection->watches = watch_list;
   connection->timeouts = timeout_list;
@@ -1189,24 +1202,15 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
   if (disconnect_link != NULL)
     _dbus_list_free_link (disconnect_link);
   
-  if (io_path_cond != NULL)
-    _dbus_condvar_free (io_path_cond);
-  
-  if (dispatch_cond != NULL)
-    _dbus_condvar_free (dispatch_cond);
-  
-  if (mutex != NULL)
-    _dbus_mutex_free (mutex);
-
-  if (io_path_mutex != NULL)
-    _dbus_mutex_free (io_path_mutex);
-
-  if (dispatch_mutex != NULL)
-    _dbus_mutex_free (dispatch_mutex);
-  
   if (connection != NULL)
-    dbus_free (connection);
-
+    {
+      _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_free (connection);
+    }
   if (pending_replies)
     _dbus_hash_table_unref (pending_replies);
   
@@ -1836,13 +1840,13 @@ _dbus_connection_last_unref (DBusConnection *connection)
 
   _dbus_list_clear (&connection->link_cache);
   
-  _dbus_condvar_free (connection->dispatch_cond);
-  _dbus_condvar_free (connection->io_path_cond);
+  _dbus_condvar_free_at_location (&connection->dispatch_cond);
+  _dbus_condvar_free_at_location (&connection->io_path_cond);
 
-  _dbus_mutex_free (connection->io_path_mutex);
-  _dbus_mutex_free (connection->dispatch_mutex);
+  _dbus_mutex_free_at_location (&connection->io_path_mutex);
+  _dbus_mutex_free_at_location (&connection->dispatch_mutex);
 
-  _dbus_mutex_free (connection->mutex);
+  _dbus_mutex_free_at_location (&connection->mutex);
   
   dbus_free (connection);
 }
@@ -3305,7 +3309,8 @@ _dbus_connection_acquire_dispatch (DBusConnection *connection)
   while (connection->dispatch_acquired)
     {
       _dbus_verbose ("%s waiting for dispatch to be acquirable\n", _DBUS_FUNCTION_NAME);
-      _dbus_condvar_wait (connection->dispatch_cond, connection->dispatch_mutex);
+      _dbus_condvar_wait (connection->dispatch_cond, 
+                          connection->dispatch_mutex);
     }
   
   _dbus_assert (!connection->dispatch_acquired);
@@ -4582,7 +4587,7 @@ dbus_bool_t
 dbus_connection_allocate_data_slot (dbus_int32_t *slot_p)
 {
   return _dbus_data_slot_allocator_alloc (&slot_allocator,
-                                          _DBUS_LOCK_NAME (connection_slots),
+                                          &_DBUS_LOCK_NAME (connection_slots),
                                           slot_p);
 }
 
index 78e94c3..d53f3bd 100644 (file)
@@ -46,7 +46,7 @@ _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator)
   allocator->allocated_slots = NULL;
   allocator->n_allocated_slots = 0;
   allocator->n_used_slots = 0;
-  allocator->lock = NULL;
+  allocator->lock_loc = NULL;
   
   return TRUE;
 }
@@ -59,26 +59,26 @@ _dbus_data_slot_allocator_init (DBusDataSlotAllocator *allocator)
  * is allocated and stored at *slot_id_p.
  * 
  * @param allocator the allocator
- * @param mutex the lock for this allocator
+ * @param mutex_loc the location lock for this allocator
  * @param slot_id_p address to fill with the slot ID
  * @returns #TRUE on success
  */
 dbus_bool_t
 _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator,
-                                 DBusMutex             *mutex,
+                                 DBusMutex             **mutex_loc,
                                  dbus_int32_t          *slot_id_p)
 {
   dbus_int32_t slot;
 
-  if (!_dbus_mutex_lock (mutex))
+  if (!_dbus_mutex_lock (*mutex_loc))
     return FALSE;
 
   if (allocator->n_allocated_slots == 0)
     {
-      _dbus_assert (allocator->lock == NULL);
-      allocator->lock = mutex;
+      _dbus_assert (allocator->lock_loc == NULL);
+      allocator->lock_loc = mutex_loc;
     }
-  else if (allocator->lock != mutex)
+  else if (allocator->lock_loc != mutex_loc)
     {
       _dbus_warn ("D-Bus threads were initialized after first using the D-Bus library. If your application does not directly initialize threads or use D-Bus, keep in mind that some library or plugin may have used D-Bus or initialized threads behind your back. You can often fix this problem by calling dbus_init_threads() or dbus_g_threads_init() early in your main() method, before D-Bus is used.");
       _dbus_assert_not_reached ("exiting");
@@ -145,7 +145,7 @@ _dbus_data_slot_allocator_alloc (DBusDataSlotAllocator *allocator,
                  slot, allocator, allocator->n_allocated_slots, allocator->n_used_slots);
   
  out:
-  _dbus_mutex_unlock (allocator->lock);
+  _dbus_mutex_unlock (*(allocator->lock_loc));
   return slot >= 0;
 }
 
@@ -164,7 +164,7 @@ void
 _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
                                 dbus_int32_t          *slot_id_p)
 {
-  _dbus_mutex_lock (allocator->lock);
+  _dbus_mutex_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);
@@ -174,7 +174,7 @@ _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
 
   if (allocator->allocated_slots[*slot_id_p].refcount > 0)
     {
-      _dbus_mutex_unlock (allocator->lock);
+      _dbus_mutex_unlock (*(allocator->lock_loc));
       return;
     }
 
@@ -189,18 +189,18 @@ _dbus_data_slot_allocator_free (DBusDataSlotAllocator *allocator,
   
   if (allocator->n_used_slots == 0)
     {
-      DBusMutex *mutex = allocator->lock;
+      DBusMutex **mutex_loc = allocator->lock_loc;
       
       dbus_free (allocator->allocated_slots);
       allocator->allocated_slots = NULL;
       allocator->n_allocated_slots = 0;
-      allocator->lock = NULL;
+      allocator->lock_loc = NULL;
 
-      _dbus_mutex_unlock (mutex);
+      _dbus_mutex_unlock (*mutex_loc);
     }
   else
     {
-      _dbus_mutex_unlock (allocator->lock);
+      _dbus_mutex_unlock (*(allocator->lock_loc));
     }
 }
 
@@ -246,11 +246,11 @@ _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.
    */
-  if (!_dbus_mutex_lock (allocator->lock))
+  if (!_dbus_mutex_lock (*(allocator->lock_loc)))
     return FALSE;
   _dbus_assert (slot < allocator->n_allocated_slots);
   _dbus_assert (allocator->allocated_slots[slot].slot_id == slot);
-  _dbus_mutex_unlock (allocator->lock);
+  _dbus_mutex_unlock (*(allocator->lock_loc));
 #endif
   
   if (slot >= list->n_slots)
@@ -304,12 +304,12 @@ _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.
    */
-  if (!_dbus_mutex_lock (allocator->lock))
+  if (!_dbus_mutex_lock (*(allocator->lock_loc)))
     return NULL;
   _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);
+  _dbus_mutex_unlock (*(allocator->lock_loc));
 #endif
 
   if (slot >= list->n_slots)
@@ -392,7 +392,7 @@ _dbus_data_slot_test (void)
 
   _dbus_data_slot_list_init (&list);
 
-  mutex = _dbus_mutex_new ();
+  _dbus_mutex_new_at_location (&mutex);
   if (mutex == NULL)
     _dbus_assert_not_reached ("failed to alloc mutex");
   
@@ -407,7 +407,7 @@ _dbus_data_slot_test (void)
        */
       dbus_int32_t tmp = -1;
       
-      _dbus_data_slot_allocator_alloc (&allocator, mutex, &tmp);
+      _dbus_data_slot_allocator_alloc (&allocator, &mutex, &tmp);
 
       if (tmp != i)
         _dbus_assert_not_reached ("did not allocate slots in numeric order\n");
@@ -472,7 +472,7 @@ _dbus_data_slot_test (void)
       ++i;
     }
 
-  _dbus_mutex_free (mutex);
+  _dbus_mutex_free_at_location (&mutex);
   
   return TRUE;
 }
index 04c8f30..72fca61 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;        /**< thread lock */
+  DBusMutex **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,
+                                             DBusMutex              **mutex_loc,
                                              int                    *slot_id_p);
 void        _dbus_data_slot_allocator_free  (DBusDataSlotAllocator  *allocator,
                                              int                    *slot_id_p);
index f032f82..9b0d7a0 100644 (file)
@@ -3595,7 +3595,7 @@ dbus_bool_t
 dbus_message_allocate_data_slot (dbus_int32_t *slot_p)
 {
   return _dbus_data_slot_allocator_alloc (&slot_allocator,
-                                          _DBUS_LOCK_NAME (message_slots),
+                                          &_DBUS_LOCK_NAME (message_slots),
                                           slot_p);
 }
 
index e6ece9d..6076d72 100644 (file)
@@ -651,7 +651,7 @@ dbus_bool_t
 dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
 {
   return _dbus_data_slot_allocator_alloc (&slot_allocator,
-                                          _DBUS_LOCK_NAME (pending_call_slots),
+                                          &_DBUS_LOCK_NAME (pending_call_slots),
                                           slot_p);
 }
 
index d1ab496..b406c86 100644 (file)
@@ -141,7 +141,7 @@ _dbus_server_init_base (DBusServer             *server,
   if (server->address == NULL)
     goto failed;
   
-  server->mutex = _dbus_mutex_new ();
+  _dbus_mutex_new_at_location (&server->mutex);
   if (server->mutex == NULL)
     goto failed;
   
@@ -160,11 +160,8 @@ _dbus_server_init_base (DBusServer             *server,
   return TRUE;
 
  failed:
-  if (server->mutex)
-    {
-      _dbus_mutex_free (server->mutex);
-      server->mutex = NULL;
-    }
+  _dbus_mutex_free_at_location (&server->mutex);
+  server->mutex = NULL;
   if (server->watches)
     {
       _dbus_watch_list_free (server->watches);
@@ -210,7 +207,7 @@ _dbus_server_finalize_base (DBusServer *server)
   _dbus_watch_list_free (server->watches);
   _dbus_timeout_list_free (server->timeouts);
 
-  _dbus_mutex_free (server->mutex);
+  _dbus_mutex_free_at_location (&server->mutex);
   
   dbus_free (server->address);
 
@@ -1060,7 +1057,7 @@ dbus_bool_t
 dbus_server_allocate_data_slot (dbus_int32_t *slot_p)
 {
   return _dbus_data_slot_allocator_alloc (&slot_allocator,
-                                          _DBUS_LOCK_NAME (server_slots),
+                                          (DBusMutex **)&_DBUS_LOCK_NAME (server_slots),
                                           slot_p);
 }
 
index 8cc3a7a..700c4f7 100644 (file)
 
 DBUS_BEGIN_DECLS
 
-DBusMutex*   _dbus_mutex_new            (void);
-void         _dbus_mutex_free           (DBusMutex                 *mutex);
-dbus_bool_t  _dbus_mutex_lock           (DBusMutex                 *mutex);
-dbus_bool_t  _dbus_mutex_unlock         (DBusMutex                 *mutex);
-
-DBusCondVar* _dbus_condvar_new          (void);
-void         _dbus_condvar_free         (DBusCondVar               *cond);
-void         _dbus_condvar_wait         (DBusCondVar               *cond,
-                                       DBusMutex                 *mutex);
-dbus_bool_t  _dbus_condvar_wait_timeout (DBusCondVar               *cond,
-                                       DBusMutex                 *mutex,
-                                       int                        timeout_milliseconds);
-void         _dbus_condvar_wake_one     (DBusCondVar               *cond);
-void         _dbus_condvar_wake_all     (DBusCondVar               *cond);
+DBusMutex*   _dbus_mutex_new                 (void);
+void         _dbus_mutex_free                (DBusMutex         *mutex);
+dbus_bool_t  _dbus_mutex_lock                (DBusMutex         *mutex);
+dbus_bool_t  _dbus_mutex_unlock              (DBusMutex         *mutex);
+void         _dbus_mutex_new_at_location     (DBusMutex        **location_p);
+void         _dbus_mutex_free_at_location    (DBusMutex        **location_p);
 
+DBusCondVar* _dbus_condvar_new               (void);
+void         _dbus_condvar_free              (DBusCondVar       *cond);
+void         _dbus_condvar_wait              (DBusCondVar       *cond,
+                                              DBusMutex         *mutex);
+dbus_bool_t  _dbus_condvar_wait_timeout      (DBusCondVar       *cond,
+                                              DBusMutex         *mutex,
+                                              int                timeout_milliseconds);
+void          _dbus_condvar_wake_one         (DBusCondVar       *cond);
+void          _dbus_condvar_wake_all         (DBusCondVar       *cond);
+void          _dbus_condvar_new_at_location  (DBusCondVar      **location_p);
+void          _dbus_condvar_free_at_location (DBusCondVar      **location_p);
 
 DBUS_END_DECLS
 
index f22d0a6..7826963 100644 (file)
@@ -23,6 +23,7 @@
 #include "dbus-threads.h"
 #include "dbus-internals.h"
 #include "dbus-threads-internal.h"
+#include "dbus-list.h"
 
 static DBusThreadFunctions thread_functions =
 {
@@ -35,6 +36,9 @@ static DBusThreadFunctions thread_functions =
 };
 static int thread_init_generation = 0;
 
+static DBusList *uninitialized_mutex_list = NULL;
+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_MUTEX ((DBusMutex*)0xABCDEF)
 
@@ -69,6 +73,32 @@ _dbus_mutex_new (void)
 }
 
 /**
+ * This does the same thing as _dbus_mutex_new.  It however
+ * gives another level of indirection by allocating a pointer
+ * to point to the mutex location.  This 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_mutex_new_at_location (DBusMutex **location_p)
+{
+  _dbus_assert (location_p != NULL);
+
+  *location_p = _dbus_mutex_new();
+
+  if (thread_init_generation != _dbus_current_generation && *location_p)
+    {
+      if (!_dbus_list_append (&uninitialized_mutex_list, location_p))
+        {
+         _dbus_mutex_free (*location_p);
+         *location_p = NULL;
+       }
+    }
+}
+
+/**
  * Frees a mutex created with dbus_mutex_new(); does
  * nothing if passed a #NULL pointer.
  */
@@ -80,6 +110,23 @@ _dbus_mutex_free (DBusMutex *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)
+{
+  if (location_p)
+    {
+      if (thread_init_generation != _dbus_current_generation)
+        _dbus_list_remove (&uninitialized_mutex_list, location_p);
+
+      _dbus_mutex_free (*location_p);
+    }
+}
+
+/**
  * Locks a mutex. Does nothing if passed a #NULL pointer.
  * Locks are not recursive.
  *
@@ -125,6 +172,33 @@ _dbus_condvar_new (void)
     return _DBUS_DUMMY_CONDVAR;
 }
 
+
+/**
+ * This does the same thing as _dbus_condvar_new.  It however
+ * gives another level of indirection by allocating a pointer
+ * to point to the condvar location.  This allows the threading
+ * module to swap out dummy condvars for real a real condvar so libraries
+ * can initialize threads even after the D-Bus API has been used.
+ *
+ * @returns the location of a new condvar or #NULL on OOM
+ */
+
+void 
+_dbus_condvar_new_at_location (DBusCondVar **location_p)
+{
+  *location_p = _dbus_condvar_new();
+
+  if (thread_init_generation != _dbus_current_generation && *location_p)
+    {
+      if (!_dbus_list_append (&uninitialized_condvar_list, location_p))
+        {
+          _dbus_condvar_free (*location_p);
+          *location_p = NULL;
+        }
+    }
+}
+
+
 /**
  * Frees a conditional variable created with dbus_condvar_new(); does
  * nothing if passed a #NULL pointer.
@@ -137,6 +211,23 @@ _dbus_condvar_free (DBusCondVar *cond)
 }
 
 /**
+ * Frees a conditional variable and removes it from the 
+ * uninitialized_condvar_list; 
+ * does nothing if passed a #NULL pointer.
+ */
+void
+_dbus_condvar_free_at_location (DBusCondVar **location_p)
+{
+  if (location_p)
+    {
+      if (thread_init_generation != _dbus_current_generation)
+        _dbus_list_remove (&uninitialized_condvar_list, location_p);
+
+      _dbus_condvar_free (*location_p);
+    }
+}
+
+/**
  * Atomically unlocks the mutex and waits for the conditions
  * variable to be signalled. Locks the mutex again before
  * returning.
@@ -214,8 +305,100 @@ shutdown_global_locks (void *data)
   dbus_free (locks);
 }
 
+static void
+shutdown_uninitialized_locks (void *data)
+{
+  _dbus_list_clear (&uninitialized_mutex_list);
+  _dbus_list_clear (&uninitialized_condvar_list);
+}
+
 static dbus_bool_t
-init_global_locks (void)
+init_uninitialized_locks (void)
+{
+  DBusList *link;
+
+  _dbus_assert (thread_init_generation == 0);
+
+  link = uninitialized_mutex_list;
+  while (link != NULL)
+    {
+      DBusMutex **mp;
+
+      mp = (DBusMutex **)link->data;
+      _dbus_assert (*mp == _DBUS_DUMMY_MUTEX);
+
+      *mp = _dbus_mutex_new ();
+      if (*mp == NULL)
+        goto fail_mutex;
+
+      link = _dbus_list_get_next_link (&uninitialized_mutex_list, link);
+    }
+
+  link = uninitialized_condvar_list;
+  while (link != NULL)
+    {
+      DBusCondVar **cp;
+
+      cp = (DBusCondVar **)link->data;
+      _dbus_assert (*cp == _DBUS_DUMMY_CONDVAR);
+
+      *cp = _dbus_condvar_new ();
+      if (*cp == NULL)
+        goto fail_condvar;
+
+      link = _dbus_list_get_next_link (&uninitialized_condvar_list, link);
+    }
+
+  _dbus_list_clear (&uninitialized_mutex_list);
+  _dbus_list_clear (&uninitialized_condvar_list);
+
+  if (!_dbus_register_shutdown_func (shutdown_uninitialized_locks,
+                                     NULL))
+    goto fail_condvar;
+
+  return TRUE;
+
+ fail_condvar:
+  link = uninitialized_condvar_list;
+  while (link != NULL)
+    {
+      DBusCondVar **cp;
+
+      cp = (DBusCondVar **)link->data;
+
+      if (*cp != _DBUS_DUMMY_CONDVAR)
+        _dbus_condvar_free (*cp);
+      else
+        break;
+
+      *cp = _DBUS_DUMMY_CONDVAR;
+
+      link = _dbus_list_get_next_link (&uninitialized_condvar_list, link);
+    }
+
+ fail_mutex:
+  link = uninitialized_mutex_list;
+  while (link != NULL)
+    {
+      DBusMutex **mp;
+
+      mp = (DBusMutex **)link->data;
+
+      if (*mp != _DBUS_DUMMY_MUTEX)
+        _dbus_mutex_free (*mp);
+      else
+        break;
+
+      *mp = _DBUS_DUMMY_MUTEX;
+
+      link = _dbus_list_get_next_link (&uninitialized_mutex_list, link);
+    }
+
+  return FALSE;
+}
+
+static dbus_bool_t
+init_locks (void)
 {
   int i;
   DBusMutex ***dynamic_global_locks;
@@ -260,6 +443,9 @@ init_global_locks (void)
   if (!_dbus_register_shutdown_func (shutdown_global_locks,
                                      dynamic_global_locks))
     goto failed;
+
+  if (!init_uninitialized_locks ())
+    goto failed;
   
   return TRUE;
 
@@ -339,24 +525,12 @@ dbus_threads_init (const DBusThreadFunctions *functions)
 
   if (thread_init_generation != _dbus_current_generation)
     thread_functions.mask = 0; /* allow re-init in new generation */
-  
+  /* Silently allow multiple init
+   * First init wins and D-Bus will always use its threading system 
+   */ 
   if (thread_functions.mask != 0)
-    {
-      /* Silently allow multiple init if the functions are the same ones.
-       * Well, we only bother checking two of them, just out of laziness.
-       */
-      if (thread_functions.mask == functions->mask &&
-          thread_functions.mutex_new == functions->mutex_new &&
-          thread_functions.condvar_new == functions->condvar_new)
-        {
-          return TRUE;
-        }
-      else
-        {
-          _dbus_warn ("dbus_threads_init() called twice with two different sets of functions\n");
-          return FALSE;
-        }
-    }
+    return TRUE;
   
   thread_functions.mutex_new = functions->mutex_new;
   thread_functions.mutex_free = functions->mutex_free;
@@ -372,7 +546,7 @@ dbus_threads_init (const DBusThreadFunctions *functions)
   
   thread_functions.mask = functions->mask;
 
-  if (!init_global_locks ())
+  if (!init_locks ())
     return FALSE;
 
   thread_init_generation = _dbus_current_generation;
index 34dd85a..41bb24b 100644 (file)
@@ -16,10 +16,10 @@ if DBUS_BUILD_TESTS
 
 ## we use noinst_PROGRAMS not check_PROGRAMS for TESTS so that we
 ## build even when not doing "make check"
-noinst_PROGRAMS=test-names test-pending-call-dispatch 
+noinst_PROGRAMS=test-names test-pending-call-dispatch test-threads-init
 
 test_names_SOURCES=                            \
-       test-names.c                             
+       test-names.c
 
 test_names_LDADD=$(top_builddir)/dbus/libdbus-1.la $(top_builddir)/dbus/libdbus-convenience.la
 
@@ -29,5 +29,10 @@ test_pending_call_dispatch_SOURCES =         \
 test_pending_call_dispatch_LDADD=$(top_builddir)/dbus/libdbus-1.la $(top_builddir)/dbus/libdbus-convenience.la
 
 
+test_threads_init_SOURCES =            \
+       test-threads-init.c
+
+test_threads_init_LDADD=$(top_builddir)/dbus/libdbus-1.la $(top_builddir)/dbus/libdbus-convenience.la
+
 endif
 
index 6c61249..81e2e84 100755 (executable)
@@ -31,3 +31,6 @@ libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/name-test/test-names || di
 
 echo "running test-pending-call-dispatch"
 libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/name-test/test-pending-call-dispatch || die "test-client failed"
+
+echo "running test-threads-init"
+libtool --mode=execute $DEBUG $DBUS_TOP_BUILDDIR/test/name-test/test-threads-init || die "test-client failed"
diff --git a/test/name-test/test-threads-init.c b/test/name-test/test-threads-init.c
new file mode 100644 (file)
index 0000000..e6743cc
--- /dev/null
@@ -0,0 +1,181 @@
+/**
+* Test to make sure late thread initialization works
+**/
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-sysdeps.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <dbus/dbus-internals.h>
+
+static void
+_run_iteration (DBusConnection *conn)
+{
+  DBusPendingCall *echo_pending;
+  DBusPendingCall *dbus_pending;
+  DBusMessage *method;
+  DBusMessage *reply;
+  char *echo = "echo";
+
+  /* send the first message */
+  method = dbus_message_new_method_call ("org.freedesktop.DBus.TestSuiteEchoService",
+                                         "/org/freedesktop/TestSuite",
+                                         "org.freedesktop.TestSuite",
+                                         "Echo");
+
+  dbus_message_append_args (method, DBUS_TYPE_STRING, &echo, NULL);
+  dbus_connection_send_with_reply (conn, method, &echo_pending, -1);
+  dbus_message_unref (method);
+  
+  /* send the second message */
+  method = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+                                         DBUS_PATH_DBUS,
+                                         "org.freedesktop.Introspectable",
+                                         "Introspect");
+
+  dbus_connection_send_with_reply (conn, method, &dbus_pending, -1);
+  dbus_message_unref (method);
+
+  /* block on the second message (should return immediately) */
+  dbus_pending_call_block (dbus_pending);
+
+  /* block on the first message */
+  /* if it does not return immediately chances 
+     are we hit the block in poll bug */
+  dbus_pending_call_block (echo_pending);
+
+  /* check the reply only to make sure we
+     are not getting errors unrelated
+     to the block in poll bug */
+  reply = dbus_pending_call_steal_reply (echo_pending);
+
+  if (reply == NULL)
+    {
+      printf ("Failed: Reply is NULL ***\n");
+      exit (1);
+    }
+
+  if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR)
+    {
+      printf ("Failed: Reply is error: %s ***\n", dbus_message_get_error_name (reply));
+      exit (1);
+    } 
+
+  dbus_message_unref (reply);
+  dbus_pending_call_unref (dbus_pending);
+  dbus_pending_call_unref (echo_pending);
+  
+}
+static void
+check_mutex_lock (DBusMutex *mutex1, 
+                  DBusMutex *mutex2, 
+                  dbus_bool_t is_same)
+{
+  _dbus_assert (mutex1 != NULL);
+  _dbus_assert (mutex2 != NULL);
+  
+  if (is_same)
+    {
+      _dbus_assert (mutex1 == mutex2);
+    }
+  else
+    {
+      _dbus_assert (mutex1 != mutex2);
+    }
+}
+
+static void
+check_condvar_lock (DBusCondVar *condvar1,  
+                    DBusCondVar *condvar2,   
+                    dbus_bool_t is_same)
+{
+  _dbus_assert (condvar1 != NULL);
+  _dbus_assert (condvar2 != NULL);
+
+  if (is_same)
+    {
+      _dbus_assert (condvar1 == condvar2);
+    }
+  else
+    {
+      _dbus_assert (condvar1 != condvar2);
+    }
+}
+
+
+int
+main (int argc, char *argv[])
+{
+  long start_tv_sec, start_tv_usec;
+  long end_tv_sec, end_tv_usec;
+  int i;
+  DBusMessage *method;
+  DBusConnection *conn;
+  DBusError error;
+  DBusMutex *mutex1, *dispatch_mutex1, *io_path_mutex1;
+  DBusCondVar *dispatch_cond1, *io_path_cond1;
+  DBusMutex *mutex2, *dispatch_mutex2, *io_path_mutex2;
+  DBusCondVar *dispatch_cond2, *io_path_cond2;
+
+  printf ("*** Testing late thread init\n");
+
+  dbus_error_init (&error);
+
+  conn = dbus_bus_get (DBUS_BUS_SESSION, &error);
+
+  _dbus_connection_test_get_locks (conn, &mutex1, 
+                                         &dispatch_mutex1, 
+                                         &io_path_mutex1,
+                                         &dispatch_cond1,
+                                         &io_path_cond1);
+  _run_iteration (conn);
+  _dbus_connection_test_get_locks (conn, &mutex2,
+                                         &dispatch_mutex2,
+                                         &io_path_mutex2,
+                                         &dispatch_cond2,
+                                         &io_path_cond2);
+
+  check_mutex_lock (mutex1, mutex2, TRUE);
+  check_mutex_lock (dispatch_mutex1, dispatch_mutex2, TRUE);
+  check_mutex_lock (io_path_mutex1, io_path_mutex2, TRUE);
+  check_condvar_lock (dispatch_cond1, dispatch_cond2, TRUE);
+  check_condvar_lock (io_path_cond1, io_path_cond2, TRUE);
+
+  _dbus_threads_init_debug ();
+
+  _dbus_connection_test_get_locks (conn, &mutex1,
+                                         &dispatch_mutex1,
+                                         &io_path_mutex1,
+                                         &dispatch_cond1,
+                                         &io_path_cond1);
+
+  check_mutex_lock (mutex1, mutex2, FALSE);
+  check_mutex_lock (dispatch_mutex1, dispatch_mutex2, FALSE);
+  check_mutex_lock (io_path_mutex1, io_path_mutex2, FALSE);
+  check_condvar_lock (dispatch_cond1, dispatch_cond2, FALSE);
+  check_condvar_lock (io_path_cond1, io_path_cond2, FALSE);
+
+  _run_iteration (conn);
+  _dbus_connection_test_get_locks (conn, &mutex2,
+                                         &dispatch_mutex2,
+                                         &io_path_mutex2,
+                                         &dispatch_cond2,
+                                         &io_path_cond2);
+
+  check_mutex_lock (mutex1, mutex2, TRUE);
+  check_mutex_lock (dispatch_mutex1, dispatch_mutex2, TRUE);
+  check_mutex_lock (io_path_mutex1, io_path_mutex2, TRUE);
+  check_condvar_lock (dispatch_cond1, dispatch_cond2, TRUE);
+  check_condvar_lock (io_path_cond1, io_path_cond2, TRUE);
+
+  method = dbus_message_new_method_call ("org.freedesktop.TestSuiteEchoService",
+                                         "/org/freedesktop/TestSuite",
+                                         "org.freedesktop.TestSuite",
+                                         "Exit");
+  dbus_connection_send (conn, method, NULL);
+  dbus_message_unref (method);
+
+  printf ("Success ***\n");
+  exit (0);
+}