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); +} |