summaryrefslogtreecommitdiffstats
path: root/dbus/dbus-threads.c
diff options
context:
space:
mode:
authorJohn (J5) Palmieri <johnp@redhat.com>2006-08-16 22:30:15 +0000
committerJohn (J5) Palmieri <johnp@redhat.com>2006-08-16 22:30:15 +0000
commit14cc7d28a8308060428bbc9b3dd357eaea3a4749 (patch)
tree3b2a02f31eb74f3844ad8dcef0fca1d29151a9ed /dbus/dbus-threads.c
parent5b5da5297552919578266714fa7abf1e45d1131d (diff)
* 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 (init_uninitialized_locks): Atomic method which goes through the static lists of mutex and condvar location and updates them with actuall locks (init_global_locks): changed to init_locks * dbus/dbus-connection.c: (_dbus_connection_test_get_locks): New method for tests to check connections (_dbus_connection_new_for_transport): Use the new at_location mutex and condvar API (dbus_connection_allocate_data_slot): Pass in the global lock address to _dbus_data_slot_allocator_alloc * dbus/dbus-dataslot.c: (_dbus_data_slot_allocator_alloc): Use the address of the mutex instead of the mutex itself * dbus/dbus-message.c: (dbus_message_allocate_data_slot): Pass in the global lock address to _dbus_data_slot_allocator_alloc * dbus/dbus-pending-call.c: (dbus_pending_call_allocate_data_slot): Pass in the global lock address to _dbus_data_slot_allocator_alloc * dbus/dbus-server.c: (_dbus_server_init_base): Use the new at_location mutex API (dbus_server_allocate_data_slot): Pass in the global lock address to _dbus_data_slot_allocator_alloc * test/name-test/test-threads-init.c: New test case for late thread initialization
Diffstat (limited to 'dbus/dbus-threads.c')
-rw-r--r--dbus/dbus-threads.c212
1 files changed, 193 insertions, 19 deletions
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;