From fe4018941190f8bf020e4a8ed2999c212e0e113d Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Sat, 15 Feb 2003 16:25:08 +0000 Subject: 2003-02-15 Alexander Larsson * dbus/dbus-threads.c: * dbus/dbus-threads.h: Add condvars. Remove static mutext from API. Implement static mutexes by initializing them from threads_init. * glib/dbus-gthread.c: * qt/dbus-qthread.cpp: Update with the thread api changes. * dbus/dbus-list.c: * dbus/dbus-list.h: Turn StaticMutex into normal mutex + init function. Export new functions _dbus_list_alloc_link, _dbus_list_free_link, _dbus_list_append_link, _dbus_list_prepend_link * dbus/dbus-sysdeps.c: * dbus/dbus-sysdeps.h: New type dbus_atomic_t, and new functions _dbus_atomic_inc, _dbus_atomic_dec. Only slow fallback implementation at the moment. * dbus/dbus-protocol.h: Add DBUS_MESSAGE_LOCAL_DISCONNECT define * dbus/dbus-message.c: Make ref/unref atomic. Fix some docs. * dbus/dbus-connection-internal.h: * dbus/dbus-connection.c: * dbus/dbus-connection.h: Make threadsafe. Change _peek to _borrow,_return & _steal_borrowed. Change disconnect callback to event. Make dbus_connection_dispatch_messages reentrant. * dbus/dbus-transport.c: Don't ref the connection on calls to the transport implementation. * dbus/dbus-message-handler.c: Make threadsafe. * glib/dbus-gmain.c: Don't use peek_message anymore * test/Makefile.am: * test/debug-thread.c: * test/debug-thread.h: Simple thread implementation that asserts() on deadlocks in single-threaded code. * test/bus-test.c: (main) Call debug_threads_init. * test/watch.c: Use disconnect message instead of disconnect callback. * bus/connection.c: * bus/connection.h: Don't call dbus_connection_set_disconnect_function. Instead export bus_connection_disconnect. * bus/dispatch.c: Call bus_connection_disconnect when we get a disconnected message. --- dbus/dbus-threads.c | 216 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 153 insertions(+), 63 deletions(-) (limited to 'dbus/dbus-threads.c') diff --git a/dbus/dbus-threads.c b/dbus/dbus-threads.c index b132e8a4..4df2e34c 100644 --- a/dbus/dbus-threads.c +++ b/dbus/dbus-threads.c @@ -27,16 +27,18 @@ static DBusThreadFunctions thread_functions = { 0, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -static DBusMutex *static_mutex_init_lock = NULL; - /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */ #define _DBUS_DUMMY_MUTEX ((void*)0xABCDEF) +/** This is used for the no-op default mutex pointer, just to be distinct from #NULL */ +#define _DBUS_DUMMY_CONDVAR ((void*)0xABCDEF2) + /** * @defgroup DBusThreads Thread functions * @ingroup DBus @@ -104,6 +106,130 @@ dbus_mutex_unlock (DBusMutex *mutex) return TRUE; } +/** + * Creates a new condition variable using the function supplied + * to dbus_threads_init(), or creates a no-op condition variable + * if threads are not initialized. May return #NULL even if + * threads are initialized, indicating out-of-memory. + * + * @returns new mutex or #NULL + */ +DBusCondVar * +dbus_condvar_new (void) +{ + if (thread_functions.condvar_new) + return (* thread_functions.condvar_new) (); + else + return _DBUS_DUMMY_MUTEX; +} + +/** + * Frees a conditional variable created with dbus_condvar_new(); does + * nothing if passed a #NULL pointer. + */ +void +dbus_condvar_free (DBusCondVar *cond) +{ + if (cond && thread_functions.condvar_free) + (* thread_functions.condvar_free) (cond); +} + +/** + * Atomically unlocks the mutex and waits for the conditions + * variable to be signalled. Locks the mutex again before + * returning. + * Does nothing if passed a #NULL pointer. + */ +void +dbus_condvar_wait (DBusCondVar *cond, + DBusMutex *mutex) +{ + if (cond && mutex && thread_functions.condvar_wait) + (* thread_functions.condvar_wait) (cond, mutex); +} + +/** + * Atomically unlocks the mutex and waits for the conditions + * variable to be signalled, or for a timeout. Locks the + * mutex again before returning. + * Does nothing if passed a #NULL pointer. + * + * @param timeout_milliseconds the maximum time to wait + * @returns TRUE if the condition was reached, or FALSE if the + * timeout was reached. + */ +dbus_bool_t +dbus_condvar_wait_timeout (DBusCondVar *cond, + DBusMutex *mutex, + int timeout_milliseconds) +{ + if (cond && mutex && thread_functions.condvar_wait) + return (* thread_functions.condvar_wait_timeout) (cond, mutex, timeout_milliseconds); + else + return FALSE; +} + +/** + * If there are threads waiting on the condition variable, wake + * up exactly one. + * Does nothing if passed a #NULL pointer. + */ +void +dbus_condvar_wake_one (DBusCondVar *cond) +{ + if (cond && thread_functions.condvar_wake_one) + (* thread_functions.condvar_wake_one) (cond); +} + +/** + * If there are threads waiting on the condition variable, wake + * up all of them. + * Does nothing if passed a #NULL pointer. + */ +void +dbus_condvar_wake_all (DBusCondVar *cond) +{ + if (cond && thread_functions.condvar_wake_all) + (* thread_functions.condvar_wake_all) (cond); +} + + +DBusMutex * _dbus_list_init_lock (void); +DBusMutex * _dbus_allocated_slots_init_lock (void); +DBusMutex *_dbus_atomic_init_lock (void); +DBusMutex *_dbus_message_handler_init_lock (void); + +static dbus_bool_t +init_static_locks(void) +{ + int i; + + struct { + DBusMutex *(*init_func)(void); + DBusMutex *mutex; + } static_locks[] = { + {&_dbus_list_init_lock}, + {&_dbus_allocated_slots_init_lock}, + {&_dbus_atomic_init_lock}, + {&_dbus_message_handler_init_lock}, + }; + + for (i = 0; i < _DBUS_N_ELEMENTS (static_locks); i++) + { + static_locks[i].mutex = (*static_locks[i].init_func)(); + + if (static_locks[i].mutex == NULL) + { + for (i = i - 1; i >= 0; i--) + dbus_mutex_free (static_locks[i].mutex); + return FALSE; + } + + } + return TRUE; +} + + /** * Initializes threads. If this function is not called, * the D-BUS library will not lock any data structures. @@ -125,14 +251,26 @@ dbus_threads_init (const DBusThreadFunctions *functions) /* these base functions are required. Future additions to * DBusThreadFunctions may be optional. */ - _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_NEW_MASK); - _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_FREE_MASK); - _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_LOCK_MASK); - _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_UNLOCK_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK); + _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK); _dbus_assert (functions->mutex_new != NULL); _dbus_assert (functions->mutex_free != NULL); _dbus_assert (functions->mutex_lock != NULL); _dbus_assert (functions->mutex_unlock != NULL); + _dbus_assert (functions->condvar_new != NULL); + _dbus_assert (functions->condvar_free != NULL); + _dbus_assert (functions->condvar_wait != NULL); + _dbus_assert (functions->condvar_wait_timeout != NULL); + _dbus_assert (functions->condvar_wake_one != NULL); + _dbus_assert (functions->condvar_wake_all != NULL); /* Check that all bits in the mask actually are valid mask bits. * ensures people won't write code that breaks when we add @@ -151,67 +289,19 @@ dbus_threads_init (const DBusThreadFunctions *functions) thread_functions.mutex_lock = functions->mutex_lock; thread_functions.mutex_unlock = functions->mutex_unlock; - thread_functions.mask = functions->mask; - - static_mutex_init_lock = dbus_mutex_new (); - - if (static_mutex_init_lock == NULL) - { - thread_functions.mask = 0; - return FALSE; - } - - return TRUE; -} - -/** Accesses the field of DBusStaticMutex that - * stores the DBusMutex used to implement. - */ -#define _DBUS_STATIC_MUTEX_IMPL(mutex) ((mutex)->pad1) - -/** - * Lock a static mutex - * - * @todo currently broken on some platforms due to - * non-workingness of "double checked locking" - * see http://bugzilla.gnome.org/show_bug.cgi?id=69668 - * and http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html - * for example. - * - * @param mutex the mutex to lock - * @returns #TRUE on success - */ -dbus_bool_t -dbus_static_mutex_lock (DBusStaticMutex *mutex) -{ - if (_DBUS_STATIC_MUTEX_IMPL (mutex)) - return dbus_mutex_lock (_DBUS_STATIC_MUTEX_IMPL (mutex)); - - if (!dbus_mutex_lock (static_mutex_init_lock)) - return FALSE; - - if (_DBUS_STATIC_MUTEX_IMPL (mutex) == NULL) - _DBUS_STATIC_MUTEX_IMPL (mutex) = dbus_mutex_new (); + thread_functions.condvar_new = functions->condvar_new; + thread_functions.condvar_free = functions->condvar_free; + thread_functions.condvar_wait = functions->condvar_wait; + thread_functions.condvar_wait_timeout = functions->condvar_wait_timeout; + thread_functions.condvar_wake_one = functions->condvar_wake_one; + thread_functions.condvar_wake_all = functions->condvar_wake_all; - dbus_mutex_unlock (static_mutex_init_lock); + thread_functions.mask = functions->mask; - if (_DBUS_STATIC_MUTEX_IMPL (mutex)) - return dbus_mutex_lock (_DBUS_STATIC_MUTEX_IMPL (mutex)); - else + if (!init_static_locks ()) return FALSE; -} - -/** - * Unlock a static mutex - * @param mutex the mutex to lock - * @returns #TRUE on success - */ -dbus_bool_t -dbus_static_mutex_unlock (DBusStaticMutex *mutex) -{ - _dbus_assert (_DBUS_STATIC_MUTEX_IMPL (mutex) != NULL); - return dbus_mutex_unlock (_DBUS_STATIC_MUTEX_IMPL (mutex)); + return TRUE; } /** @} */ -- cgit