summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog42
-rw-r--r--dbus/dbus-connection.c105
-rw-r--r--dbus/dbus-dataslot.c42
-rw-r--r--dbus/dbus-dataslot.h4
-rw-r--r--dbus/dbus-message.c2
-rw-r--r--dbus/dbus-pending-call.c2
-rw-r--r--dbus/dbus-server.c13
-rw-r--r--dbus/dbus-threads-internal.h31
-rw-r--r--dbus/dbus-threads.c212
-rw-r--r--test/name-test/Makefile.am9
-rwxr-xr-xtest/name-test/run-test.sh3
-rw-r--r--test/name-test/test-threads-init.c181
12 files changed, 528 insertions, 118 deletions
diff --git a/ChangeLog b/ChangeLog
index 14f89ed3..a394d5cc 100644
--- 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):
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);
+}