diff options
| -rw-r--r-- | ChangeLog | 42 | ||||
| -rw-r--r-- | dbus/dbus-connection.c | 105 | ||||
| -rw-r--r-- | dbus/dbus-dataslot.c | 42 | ||||
| -rw-r--r-- | dbus/dbus-dataslot.h | 4 | ||||
| -rw-r--r-- | dbus/dbus-message.c | 2 | ||||
| -rw-r--r-- | dbus/dbus-pending-call.c | 2 | ||||
| -rw-r--r-- | dbus/dbus-server.c | 13 | ||||
| -rw-r--r-- | dbus/dbus-threads-internal.h | 31 | ||||
| -rw-r--r-- | dbus/dbus-threads.c | 212 | ||||
| -rw-r--r-- | test/name-test/Makefile.am | 9 | ||||
| -rwxr-xr-x | test/name-test/run-test.sh | 3 | ||||
| -rw-r--r-- | test/name-test/test-threads-init.c | 181 | 
12 files changed, 528 insertions, 118 deletions
| @@ -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): diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 2e942064..27720998 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -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);  } diff --git a/dbus/dbus-dataslot.c b/dbus/dbus-dataslot.c index 78e94c37..d53f3bd7 100644 --- a/dbus/dbus-dataslot.c +++ b/dbus/dbus-dataslot.c @@ -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;  } diff --git a/dbus/dbus-dataslot.h b/dbus/dbus-dataslot.h index 04c8f309..72fca619 100644 --- a/dbus/dbus-dataslot.h +++ b/dbus/dbus-dataslot.h @@ -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); diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index f032f82a..9b0d7a0b 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -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);  } diff --git a/dbus/dbus-pending-call.c b/dbus/dbus-pending-call.c index e6ece9dd..6076d723 100644 --- a/dbus/dbus-pending-call.c +++ b/dbus/dbus-pending-call.c @@ -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);  } diff --git a/dbus/dbus-server.c b/dbus/dbus-server.c index d1ab496a..b406c869 100644 --- a/dbus/dbus-server.c +++ b/dbus/dbus-server.c @@ -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);  } diff --git a/dbus/dbus-threads-internal.h b/dbus/dbus-threads-internal.h index 8cc3a7ac..700c4f75 100644 --- a/dbus/dbus-threads-internal.h +++ b/dbus/dbus-threads-internal.h @@ -29,21 +29,24 @@  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 diff --git a/dbus/dbus-threads.c b/dbus/dbus-threads.c index f22d0a68..78269634 100644 --- a/dbus/dbus-threads.c +++ b/dbus/dbus-threads.c @@ -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; diff --git a/test/name-test/Makefile.am b/test/name-test/Makefile.am index 34dd85a8..41bb24be 100644 --- a/test/name-test/Makefile.am +++ b/test/name-test/Makefile.am @@ -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 diff --git a/test/name-test/run-test.sh b/test/name-test/run-test.sh index 6c612499..81e2e84e 100755 --- a/test/name-test/run-test.sh +++ b/test/name-test/run-test.sh @@ -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 index 00000000..e6743cce --- /dev/null +++ b/test/name-test/test-threads-init.c @@ -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); +} | 
