diff options
author | Havoc Pennington <hp@redhat.com> | 2006-10-27 01:09:24 +0000 |
---|---|---|
committer | Havoc Pennington <hp@redhat.com> | 2006-10-27 01:09:24 +0000 |
commit | 5886f5326da2dbe8f1a7c97736d544fa4cef169b (patch) | |
tree | dc67158b953aa57605d18ea43f1cd9f916d82716 /dbus | |
parent | caae991f97ce1b09595d3229f2868268678b8de6 (diff) |
2006-10-26 Havoc Pennington <hp@redhat.com>
* dbus/dbus-threads.[hc]: Documentation improvements. Clarify how
condition variables relate to recursive mutexes.
* dbus/dbus-sysdeps-pthread.c, dbus/dbus-sysdeps-win-thread.c,
dbus/dbus-threads.c: Split the platforms-specific thread
implementations into their own files.
* dbus/dbus-sysdeps-pthread.c
(_dbus_pthread_condvar_wait_timeout): invert the return value, it
was backward. Not that anything uses it.
Diffstat (limited to 'dbus')
-rw-r--r-- | dbus/Makefile.am | 1 | ||||
-rw-r--r-- | dbus/dbus-connection.c | 19 | ||||
-rw-r--r-- | dbus/dbus-sysdeps-pthread.c | 167 | ||||
-rw-r--r-- | dbus/dbus-sysdeps-win-thread.c | 259 | ||||
-rw-r--r-- | dbus/dbus-sysdeps.h | 6 | ||||
-rw-r--r-- | dbus/dbus-threads-internal.h | 8 | ||||
-rw-r--r-- | dbus/dbus-threads.c | 379 | ||||
-rw-r--r-- | dbus/dbus-threads.h | 39 |
8 files changed, 493 insertions, 385 deletions
diff --git a/dbus/Makefile.am b/dbus/Makefile.am index b8d97783..c6fb4755 100644 --- a/dbus/Makefile.am +++ b/dbus/Makefile.am @@ -113,6 +113,7 @@ DBUS_SHARED_SOURCES= \ dbus-string-private.h \ dbus-sysdeps.c \ dbus-sysdeps.h \ + dbus-sysdeps-pthread.c \ dbus-sysdeps-unix.c \ dbus-sysdeps-unix.h \ dbus-userdb.c \ diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index 7a20e047..e2debdd0 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -991,7 +991,7 @@ _dbus_connection_remove_pending_call (DBusConnection *connection, */ static dbus_bool_t _dbus_connection_acquire_io_path (DBusConnection *connection, - int timeout_milliseconds) + int timeout_milliseconds) { dbus_bool_t we_acquired; @@ -1017,9 +1017,20 @@ _dbus_connection_acquire_io_path (DBusConnection *connection, { _dbus_verbose ("%s waiting %d for IO path to be acquirable\n", _DBUS_FUNCTION_NAME, timeout_milliseconds); - _dbus_condvar_wait_timeout (connection->io_path_cond, - connection->io_path_mutex, - timeout_milliseconds); + + if (!_dbus_condvar_wait_timeout (connection->io_path_cond, + connection->io_path_mutex, + timeout_milliseconds)) + { + /* We timed out before anyone signaled. */ + /* (writing the loop to handle the !timedout case by + * waiting longer if needed is a pain since dbus + * wraps pthread_cond_timedwait to take a relative + * time instead of absolute, something kind of stupid + * on our part. for now it doesn't matter, we will just + * end up back here eventually.) + */ + } } else { diff --git a/dbus/dbus-sysdeps-pthread.c b/dbus/dbus-sysdeps-pthread.c new file mode 100644 index 00000000..3b7bb6d1 --- /dev/null +++ b/dbus/dbus-sysdeps-pthread.c @@ -0,0 +1,167 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-sysdeps-pthread.c Implements threads using pthreads (internal to libdbus) + * + * Copyright (C) 2002, 2003, 2006 Red Hat, Inc. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-sysdeps.h" +#include "dbus-threads.h" + +#include <sys/time.h> +#include <pthread.h> + +static DBusMutex* +_dbus_pthread_mutex_new (void) +{ + pthread_mutex_t *retval; + + retval = dbus_new (pthread_mutex_t, 1); + if (retval == NULL) + return NULL; + + if (pthread_mutex_init (retval, NULL)) + { + dbus_free (retval); + return NULL; + } + + return (DBusMutex *) retval; +} + +static void +_dbus_pthread_mutex_free (DBusMutex *mutex) +{ + pthread_mutex_destroy ((pthread_mutex_t *) mutex); + dbus_free (mutex); +} + +static dbus_bool_t +_dbus_pthread_mutex_lock (DBusMutex *mutex) +{ + return pthread_mutex_lock ((pthread_mutex_t *) mutex) == 0; +} + +static dbus_bool_t +_dbus_pthread_mutex_unlock (DBusMutex *mutex) +{ + return pthread_mutex_unlock ((pthread_mutex_t *) mutex) == 0; +} + +static DBusCondVar * +_dbus_pthread_condvar_new (void) +{ + pthread_cond_t *retval; + + retval = dbus_new (pthread_cond_t, 1); + if (retval == NULL) + return NULL; + + if (pthread_cond_init (retval, NULL)) + { + dbus_free (retval); + return NULL; + } + return (DBusCondVar *) retval; +} + +static void +_dbus_pthread_condvar_free (DBusCondVar *cond) +{ + pthread_cond_destroy ((pthread_cond_t *) cond); + dbus_free (cond); +} + +static void +_dbus_pthread_condvar_wait (DBusCondVar *cond, + DBusMutex *mutex) +{ + pthread_cond_wait ((pthread_cond_t *)cond, + (pthread_mutex_t *) mutex); +} + +static dbus_bool_t +_dbus_pthread_condvar_wait_timeout (DBusCondVar *cond, + DBusMutex *mutex, + int timeout_milliseconds) +{ + struct timeval time_now; + struct timespec end_time; + int result; + + gettimeofday (&time_now, NULL); + + end_time.tv_sec = time_now.tv_sec + timeout_milliseconds / 1000; + end_time.tv_nsec = (time_now.tv_usec + (timeout_milliseconds % 1000) * 1000) * 1000; + if (end_time.tv_nsec > 1000*1000*1000) + { + end_time.tv_sec += 1; + end_time.tv_nsec -= 1000*1000*1000; + } + + result = pthread_cond_timedwait ((pthread_cond_t *) cond, + (pthread_mutex_t *) mutex, + &end_time); + + /* return true if we did not time out */ + return result != ETIMEDOUT; +} + +static void +_dbus_pthread_condvar_wake_one (DBusCondVar *cond) +{ + pthread_cond_signal ((pthread_cond_t *)cond); +} + +static void +_dbus_pthread_condvar_wake_all (DBusCondVar *cond) +{ + pthread_cond_broadcast ((pthread_cond_t *)cond); +} + +static const DBusThreadFunctions pthread_functions = +{ + DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK| + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK, + _dbus_pthread_mutex_new, + _dbus_pthread_mutex_free, + _dbus_pthread_mutex_lock, + _dbus_pthread_mutex_unlock, + _dbus_pthread_condvar_new, + _dbus_pthread_condvar_free, + _dbus_pthread_condvar_wait, + _dbus_pthread_condvar_wait_timeout, + _dbus_pthread_condvar_wake_one, + _dbus_pthread_condvar_wake_all +}; + +dbus_bool_t +_dbus_threads_init_platform_specific (void) +{ + return dbus_threads_init (&pthread_functions); +} diff --git a/dbus/dbus-sysdeps-win-thread.c b/dbus/dbus-sysdeps-win-thread.c new file mode 100644 index 00000000..e0a51031 --- /dev/null +++ b/dbus/dbus-sysdeps-win-thread.c @@ -0,0 +1,259 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-sysdeps-pthread.c Implements threads using Windows threads (internal to libdbus) + * + * Copyright (C) 2006 Red Hat, Inc. + * + * Licensed under the Academic Free License version 2.1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "dbus-internals.h" +#include "dbus-sysdeps.h" +#include "dbus-threads.h" + +#include <windows.h> + +struct DBusCondVar { + DBusList *list; /**< list thread-local-stored events waiting on the cond variable */ + CRITICAL_SECTION lock; /**< lock protecting the list */ +}; + +static DWORD dbus_cond_event_tls = TLS_OUT_OF_INDEXES; + + +BOOL WINAPI DllMain (HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved); + +/* We need this to free the TLS events on thread exit */ +BOOL WINAPI +DllMain (HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ + HANDLE event; + switch (fdwReason) + { + case DLL_THREAD_DETACH: + if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES) + { + event = TlsGetValue(dbus_cond_event_tls); + CloseHandle (event); + TlsSetValue(dbus_cond_event_tls, NULL); + } + break; + case DLL_PROCESS_DETACH: + if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES) + { + event = TlsGetValue(dbus_cond_event_tls); + CloseHandle (event); + TlsSetValue(dbus_cond_event_tls, NULL); + + TlsFree(dbus_cond_event_tls); + } + break; + default: + break; + } + return TRUE; +} + +static DBusMutex* +_dbus_windows_mutex_new (void) +{ + HANDLE handle; + handle = CreateMutex (NULL, FALSE, NULL); + return (DBusMutex *) handle; +} + +static void +_dbus_windows_mutex_free (DBusMutex *mutex) +{ + CloseHandle ((HANDLE *) mutex); +} + +static dbus_bool_t +_dbus_windows_mutex_lock (DBusMutex *mutex) +{ + return WaitForSingleObject ((HANDLE *) mutex, INFINITE) != WAIT_FAILED; +} + +static dbus_bool_t +_dbus_windows_mutex_unlock (DBusMutex *mutex) +{ + return ReleaseMutex ((HANDLE *) mutex) != 0; +} + +static DBusCondVar * +_dbus_windows_condvar_new (void) +{ + DBusCondVar *cond; + + cond = dbus_new (DBusCondVar, 1); + if (cond == NULL) + return NULL; + + cond->list = NULL; + + InitializeCriticalSection (&cond->lock); + return (DBusCondVar *) cond; +} + +static void +_dbus_windows_condvar_free (DBusCondVar *cond) +{ + DeleteCriticalSection (&cond->lock); + _dbus_list_clear (&cond->list); + dbus_free (cond); +} + +static dbus_bool_t +_dbus_condvar_wait_win32 (DBusCondVar *cond, + DBusMutex *mutex, + int milliseconds) +{ + DWORD retval; + dbus_bool_t ret; + HANDLE event = TlsGetValue (dbus_cond_event_tls); + + if (!event) + { + event = CreateEvent (0, FALSE, FALSE, NULL); + if (event == 0) + return FALSE; + TlsSetValue (dbus_cond_event_tls, event); + } + + EnterCriticalSection (&cond->lock); + + /* The event must not be signaled. Check this */ + _dbus_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT); + + ret = _dbus_list_append (&cond->list, event); + + LeaveCriticalSection (&cond->lock); + + if (!ret) + return FALSE; /* Prepend failed */ + + _dbus_mutex_unlock (mutex); + retval = WaitForSingleObject (event, milliseconds); + _dbus_mutex_lock (mutex); + + if (retval == WAIT_TIMEOUT) + { + EnterCriticalSection (&cond->lock); + _dbus_list_remove (&cond->list, event); + + /* In the meantime we could have been signaled, so we must again + * wait for the signal, this time with no timeout, to reset + * it. retval is set again to honour the late arrival of the + * signal */ + retval = WaitForSingleObject (event, 0); + + LeaveCriticalSection (&cond->lock); + } + +#ifndef DBUS_DISABLE_ASSERT + EnterCriticalSection (&cond->lock); + + /* Now event must not be inside the array, check this */ + _dbus_assert (_dbus_list_remove (cond->list, event) == FALSE); + + LeaveCriticalSection (&cond->lock); +#endif /* !G_DISABLE_ASSERT */ + + return retval != WAIT_TIMEOUT; +} + +static void +_dbus_windows_condvar_wait (DBusCondVar *cond, + DBusMutex *mutex) +{ + _dbus_condvar_wait_win32 (cond, mutex, INFINITE); +} + +static dbus_bool_t +_dbus_windows_condvar_wait_timeout (DBusCondVar *cond, + DBusMutex *mutex, + int timeout_milliseconds) +{ + return _dbus_condvar_wait_win32 (cond, mutex, timeout_milliseconds); +} + +static void +_dbus_windows_condvar_wake_one (DBusCondVar *cond) +{ + EnterCriticalSection (&cond->lock); + + if (cond->list != NULL) + SetEvent (_dbus_list_pop_first (&cond->list)); + + LeaveCriticalSection (&cond->lock); +} + +static void +_dbus_windows_condvar_wake_all (DBusCondVar *cond) +{ + EnterCriticalSection (&cond->lock); + + while (cond->list != NULL) + SetEvent (_dbus_list_pop_first (&cond->list)); + + LeaveCriticalSection (&cond->lock); +} + +static const DBusThreadFunctions windows_functions = +{ + DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK | + DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK | + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK| + DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK, + _dbus_windows_mutex_new, + _dbus_windows_mutex_free, + _dbus_windows_mutex_lock, + _dbus_windows_mutex_unlock, + _dbus_windows_condvar_new, + _dbus_windows_condvar_free, + _dbus_windows_condvar_wait, + _dbus_windows_condvar_wait_timeout, + _dbus_windows_condvar_wake_one, + _dbus_windows_condvar_wake_all +}; + +void +_dbus_threads_init_platform_specific (void) +{ + /* We reuse this over several generations, because we can't + * free the events once they are in use + */ + if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES) + { + dbus_cond_event_tls = TlsAlloc (); + if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES) + return FALSE; + } + + return dbus_threads_init (&windows_functions); +} + diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 9e4e6544..eb395e05 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -425,6 +425,12 @@ dbus_bool_t _dbus_read_local_machine_uuid (DBusGUID *machine_id, dbus_bool_t create_if_not_found, DBusError *error); +/** + * Initialize threads as in dbus_threads_init_default(), appropriately + * for the platform. + * @returns #FALSE if no memory + */ +dbus_bool_t _dbus_threads_init_platform_specific (void); /** @} */ diff --git a/dbus/dbus-threads-internal.h b/dbus/dbus-threads-internal.h index 8f26bca1..ed30df65 100644 --- a/dbus/dbus-threads-internal.h +++ b/dbus/dbus-threads-internal.h @@ -43,10 +43,10 @@ void _dbus_condvar_wait (DBusCondVar *cond, 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); +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 98f08730..f962316b 100644 --- a/dbus/dbus-threads.c +++ b/dbus/dbus-threads.c @@ -1,7 +1,7 @@ /* -*- mode: C; c-file-style: "gnu" -*- */ /* dbus-threads.h D-Bus threads handling * - * Copyright (C) 2002, 2003 Red Hat Inc. + * Copyright (C) 2002, 2003, 2006 Red Hat Inc. * * Licensed under the Academic Free License version 2.1 * @@ -25,17 +25,6 @@ #include "dbus-threads-internal.h" #include "dbus-list.h" -#if defined(__WIN32) || defined(__CYGWIN__) -#define USE_WIN32_THREADS -#endif - -#ifdef USE_WIN32_THREADS -#include <windows.h> -#else -#include <sys/time.h> -#include <pthread.h> -#endif - static DBusThreadFunctions thread_functions = { 0, @@ -46,15 +35,6 @@ static DBusThreadFunctions thread_functions = NULL, NULL, NULL, NULL }; -#ifdef USE_WIN32_THREADS -struct DBusCondVar { - DBusList *list; - CRITICAL_SECTION lock; -}; - -static DWORD dbus_cond_event_tls = TLS_OUT_OF_INDEXES; -#endif - static int thread_init_generation = 0; static DBusList *uninitialized_mutex_list = NULL; @@ -275,16 +255,15 @@ _dbus_condvar_wait (DBusCondVar *cond, } /** - * 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. + * 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. Return value + * is #FALSE if we timed out, #TRUE otherwise. * * @param cond the condition variable * @param mutex the mutex * @param timeout_milliseconds the maximum time to wait - * @returns TRUE if the condition was reached, or FALSE if the - * timeout was reached. + * @returns #FALSE if the timeout occurred, #TRUE if not */ dbus_bool_t _dbus_condvar_wait_timeout (DBusCondVar *cond, @@ -672,338 +651,6 @@ dbus_threads_init (const DBusThreadFunctions *functions) /* Default thread implemenation */ -static DBusMutex* _dbus_internal_mutex_new (void); -static void _dbus_internal_mutex_free (DBusMutex *mutex); -static dbus_bool_t _dbus_internal_mutex_lock (DBusMutex *mutex); -static dbus_bool_t _dbus_internal_mutex_unlock (DBusMutex *mutex); -static DBusCondVar *_dbus_internal_condvar_new (void); -static void _dbus_internal_condvar_free (DBusCondVar *cond); -static void _dbus_internal_condvar_wait (DBusCondVar *cond, - DBusMutex *mutex); -static dbus_bool_t _dbus_internal_condvar_wait_timeout (DBusCondVar *cond, - DBusMutex *mutex, - int timeout_milliseconds); -static void _dbus_internal_condvar_wake_one (DBusCondVar *cond); -static void _dbus_internal_condvar_wake_all (DBusCondVar *cond); - -#ifdef USE_WIN32_THREADS - -BOOL WINAPI DllMain (HINSTANCE hinstDLL, - DWORD fdwReason, - LPVOID lpvReserved); - -/* We need this to free the TLS events on thread exit */ -BOOL WINAPI -DllMain (HINSTANCE hinstDLL, - DWORD fdwReason, - LPVOID lpvReserved) -{ - HANDLE event; - switch (fdwReason) - { - case DLL_THREAD_DETACH: - if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES) - { - event = TlsGetValue(dbus_cond_event_tls); - CloseHandle (event); - TlsSetValue(dbus_cond_event_tls, NULL); - } - break; - case DLL_PROCESS_DETACH: - if (dbus_cond_event_tls != TLS_OUT_OF_INDEXES) - { - event = TlsGetValue(dbus_cond_event_tls); - CloseHandle (event); - TlsSetValue(dbus_cond_event_tls, NULL); - - TlsFree(dbus_cond_event_tls); - } - break; - default: - break; - } - return TRUE; -} - -static DBusMutex* -_dbus_internal_mutex_new (void) -{ - HANDLE handle; - handle = CreateMutex (NULL, FALSE, NULL); - return (DBusMutex *) handle; -} - -static void -_dbus_internal_mutex_free (DBusMutex *mutex) -{ - CloseHandle ((HANDLE *) mutex); -} - -static dbus_bool_t -_dbus_internal_mutex_lock (DBusMutex *mutex) -{ - return WaitForSingleObject ((HANDLE *) mutex, INFINITE) != WAIT_FAILED; -} - -static dbus_bool_t -_dbus_internal_mutex_unlock (DBusMutex *mutex) -{ - return ReleaseMutex ((HANDLE *) mutex) != 0; -} - -static DBusCondVar * -_dbus_internal_condvar_new (void) -{ - DBusCondVar *cond; - - cond = dbus_new (DBusCondVar, 1); - if (cond == NULL) - return NULL; - - cond->list = NULL; - - InitializeCriticalSection (&cond->lock); - return (DBusCondVar *) cond; -} - -static void -_dbus_internal_condvar_free (DBusCondVar *cond) -{ - DeleteCriticalSection (&cond->lock); - _dbus_list_clear (&cond->list); - dbus_free (cond); -} - -static dbus_bool_t -_dbus_condvar_wait_win32 (DBusCondVar *cond, - DBusMutex *mutex, - int milliseconds) -{ - DWORD retval; - dbus_bool_t ret; - HANDLE event = TlsGetValue (dbus_cond_event_tls); - - if (!event) - { - event = CreateEvent (0, FALSE, FALSE, NULL); - if (event == 0) - return FALSE; - TlsSetValue (dbus_cond_event_tls, event); - } - - EnterCriticalSection (&cond->lock); - - /* The event must not be signaled. Check this */ - _dbus_assert (WaitForSingleObject (event, 0) == WAIT_TIMEOUT); - - ret = _dbus_list_append (&cond->list, event); - - LeaveCriticalSection (&cond->lock); - - if (!ret) - return FALSE; /* Prepend failed */ - - _dbus_mutex_unlock (mutex); - retval = WaitForSingleObject (event, milliseconds); - _dbus_mutex_lock (mutex); - - if (retval == WAIT_TIMEOUT) - { - EnterCriticalSection (&cond->lock); - _dbus_list_remove (&cond->list, event); - - /* In the meantime we could have been signaled, so we must again - * wait for the signal, this time with no timeout, to reset - * it. retval is set again to honour the late arrival of the - * signal */ - retval = WaitForSingleObject (event, 0); - - LeaveCriticalSection (&cond->lock); - } - -#ifndef DBUS_DISABLE_ASSERT - EnterCriticalSection (&cond->lock); - - /* Now event must not be inside the array, check this */ - _dbus_assert (_dbus_list_remove (cond->list, event) == FALSE); - - LeaveCriticalSection (&cond->lock); -#endif /* !G_DISABLE_ASSERT */ - - return retval != WAIT_TIMEOUT; -} - -static void -_dbus_internal_condvar_wait (DBusCondVar *cond, - DBusMutex *mutex) -{ - _dbus_condvar_wait_win32 (cond, mutex, INFINITE); -} - -static dbus_bool_t -_dbus_internal_condvar_wait_timeout (DBusCondVar *cond, - DBusMutex *mutex, - int timeout_milliseconds) -{ - return _dbus_condvar_wait_win32 (cond, mutex, timeout_milliseconds); -} - -static void -_dbus_internal_condvar_wake_one (DBusCondVar *cond) -{ - EnterCriticalSection (&cond->lock); - - if (cond->list != NULL) - SetEvent (_dbus_list_pop_first (&cond->list)); - - LeaveCriticalSection (&cond->lock); -} - -static void -_dbus_internal_condvar_wake_all (DBusCondVar *cond) -{ - EnterCriticalSection (&cond->lock); - - while (cond->list != NULL) - SetEvent (_dbus_list_pop_first (&cond->list)); - - LeaveCriticalSection (&cond->lock); -} - - -#else /* Posix threads */ - -static DBusMutex* -_dbus_internal_mutex_new (void) -{ - pthread_mutex_t *retval; - - retval = dbus_new (pthread_mutex_t, 1); - if (retval == NULL) - return NULL; - - if (pthread_mutex_init (retval, NULL)) - { - dbus_free (retval); - return NULL; - } - return (DBusMutex *) retval; -} - -static void -_dbus_internal_mutex_free (DBusMutex *mutex) -{ - pthread_mutex_destroy ((pthread_mutex_t *) mutex); - dbus_free (mutex); -} - -static dbus_bool_t -_dbus_internal_mutex_lock (DBusMutex *mutex) -{ - return pthread_mutex_lock ((pthread_mutex_t *) mutex) == 0; -} - -static dbus_bool_t -_dbus_internal_mutex_unlock (DBusMutex *mutex) -{ - return pthread_mutex_unlock ((pthread_mutex_t *) mutex) == 0; -} - -static DBusCondVar * -_dbus_internal_condvar_new (void) -{ - pthread_cond_t *retval; - - retval = dbus_new (pthread_cond_t, 1); - if (retval == NULL) - return NULL; - - if (pthread_cond_init (retval, NULL)) - { - dbus_free (retval); - return NULL; - } - return (DBusCondVar *) retval; -} - -static void -_dbus_internal_condvar_free (DBusCondVar *cond) -{ - pthread_cond_destroy ((pthread_cond_t *) cond); - dbus_free (cond); -} - -static void -_dbus_internal_condvar_wait (DBusCondVar *cond, - DBusMutex *mutex) -{ - pthread_cond_wait ((pthread_cond_t *)cond, - (pthread_mutex_t *) mutex); -} - -static dbus_bool_t -_dbus_internal_condvar_wait_timeout (DBusCondVar *cond, - DBusMutex *mutex, - int timeout_milliseconds) -{ - struct timeval time_now; - struct timespec end_time; - int result; - - gettimeofday (&time_now, NULL); - - end_time.tv_sec = time_now.tv_sec + timeout_milliseconds / 1000; - end_time.tv_nsec = (time_now.tv_usec + (timeout_milliseconds % 1000) * 1000) * 1000; - if (end_time.tv_nsec > 1000*1000*1000) - { - end_time.tv_sec += 1; - end_time.tv_nsec -= 1000*1000*1000; - } - - result = pthread_cond_timedwait ((pthread_cond_t *) cond, - (pthread_mutex_t *) mutex, - &end_time); - return result == ETIMEDOUT; -} - -static void -_dbus_internal_condvar_wake_one (DBusCondVar *cond) -{ - pthread_cond_signal ((pthread_cond_t *)cond); -} - -static void -_dbus_internal_condvar_wake_all (DBusCondVar *cond) -{ - pthread_cond_broadcast ((pthread_cond_t *)cond); -} - -#endif - -static const DBusThreadFunctions internal_functions = -{ - DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK | - DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK | - DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK | - DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK | - DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK | - DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK | - DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK | - DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK | - DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK| - DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK, - _dbus_internal_mutex_new, - _dbus_internal_mutex_free, - _dbus_internal_mutex_lock, - _dbus_internal_mutex_unlock, - _dbus_internal_condvar_new, - _dbus_internal_condvar_free, - _dbus_internal_condvar_wait, - _dbus_internal_condvar_wait_timeout, - _dbus_internal_condvar_wake_one, - _dbus_internal_condvar_wake_all -}; - /** * * Calls dbus_threads_init() with a default set of @@ -1022,19 +669,7 @@ static const DBusThreadFunctions internal_functions = dbus_bool_t dbus_threads_init_default (void) { -#ifdef USE_WIN32_THREADS - /* We reuse this over several generations, because we can't - * free the events once they are in use - */ - if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES) - { - dbus_cond_event_tls = TlsAlloc (); - if (dbus_cond_event_tls == TLS_OUT_OF_INDEXES) - return FALSE; - } -#endif - - return dbus_threads_init (&internal_functions); + return _dbus_threads_init_platform_specific (); } diff --git a/dbus/dbus-threads.h b/dbus/dbus-threads.h index 344b0ffd..5cb13b1a 100644 --- a/dbus/dbus-threads.h +++ b/dbus/dbus-threads.h @@ -46,13 +46,16 @@ typedef struct DBusCondVar DBusCondVar; typedef DBusMutex* (* DBusMutexNewFunction) (void); /** Deprecated, provide DBusRecursiveMutexFreeFunction instead. */ typedef void (* DBusMutexFreeFunction) (DBusMutex *mutex); -/** Deprecated, provide DBusRecursiveMutexLockFunction instead. */ +/** Deprecated, provide DBusRecursiveMutexLockFunction instead. Return value is lock success, but gets ignored in practice. */ typedef dbus_bool_t (* DBusMutexLockFunction) (DBusMutex *mutex); -/** Deprecated, provide DBusRecursiveMutexUnlockFunction instead. */ +/** Deprecated, provide DBusRecursiveMutexUnlockFunction instead. Return value is unlock success, but gets ignored in practice. */ typedef dbus_bool_t (* DBusMutexUnlockFunction) (DBusMutex *mutex); /** Creates a new recursively-lockable mutex, or returns #NULL if not enough memory. - * Found in #DBusThreadFunctions + * Found in #DBusThreadFunctions. Do not just use PTHREAD_MUTEX_RECURSIVE for this, because + * it does not save/restore the recursion count when waiting on a condition. libdbus + * requires the Java-style behavior where the mutex is fully unlocked to wait on + * a condition. */ typedef DBusMutex* (* DBusRecursiveMutexNewFunction) (void); /** Frees a recursively-lockable mutex. Found in #DBusThreadFunctions. @@ -71,11 +74,21 @@ typedef DBusCondVar* (* DBusCondVarNewFunction) (void); /** Frees a condition variable. Found in #DBusThreadFunctions. */ typedef void (* DBusCondVarFreeFunction) (DBusCondVar *cond); -/** Waits on a condition variable. Found in #DBusThreadFunctions. + +/** Waits on a condition variable. Found in + * #DBusThreadFunctions. Must work with either a recursive or + * nonrecursive mutex, whichever the thread implementation + * provides. Note that PTHREAD_MUTEX_RECURSIVE does not work with + * condition variables (does not save/restore the recursion count) so + * don't try using simply pthread_cond_wait() and a + * PTHREAD_MUTEX_RECURSIVE to implement this, it won't work right. */ typedef void (* DBusCondVarWaitFunction) (DBusCondVar *cond, DBusMutex *mutex); -/** Waits on a condition variable with a timeout. Found in #DBusThreadFunctions. + +/** Waits on a condition variable with a timeout. Found in + * #DBusThreadFunctions. Returns #TRUE if the wait did not + * time out, and #FALSE if it did. */ typedef dbus_bool_t (* DBusCondVarWaitTimeoutFunction) (DBusCondVar *cond, DBusMutex *mutex, @@ -115,6 +128,22 @@ typedef enum * Functions that must be implemented to make the D-Bus library * thread-aware. The recursive mutex functions should be specified * rather than the old, deprecated nonrecursive ones. + * + * The condition variable functions have to work with recursive + * mutexes if you provide those, or with nonrecursive mutexes if you + * provide those. + * + * If implementing threads using pthreads, be aware that + * PTHREAD_MUTEX_RECURSIVE is broken in combination with condition + * variables. libdbus relies on the Java-style behavior that when + * waiting on a condition, the recursion count is saved and restored, + * and the mutex is completely unlocked, not just decremented one + * level of recursion. + * + * Thus with pthreads you probably have to roll your own emulated + * recursive mutexes, you can't use PTHREAD_MUTEX_RECURSIVE. This is + * what dbus_threads_init_default() does on platforms that use + * pthreads. */ typedef struct { |