diff options
Diffstat (limited to '3rdparty/openpgm-svn-r1135/pgm/thread.c')
-rw-r--r-- | 3rdparty/openpgm-svn-r1135/pgm/thread.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/3rdparty/openpgm-svn-r1135/pgm/thread.c b/3rdparty/openpgm-svn-r1135/pgm/thread.c new file mode 100644 index 0000000..ad68ca3 --- /dev/null +++ b/3rdparty/openpgm-svn-r1135/pgm/thread.c @@ -0,0 +1,457 @@ +/* vim:ts=8:sts=8:sw=4:noai:noexpandtab + * + * mutexes and locks. + * + * Copyright (c) 2010 Miru Limited. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <errno.h> +#include <impl/framework.h> + +//#define THREAD_DEBUG + + +/* Globals */ + +#if defined(_WIN32) && !defined(CONFIG_HAVE_WIN_COND) +static DWORD cond_event_tls = TLS_OUT_OF_INDEXES; +#endif + +static volatile uint32_t thread_ref_count = 0; + + +#ifndef _WIN32 +# define posix_check_err(err, name) \ + do { \ + const int save_error = (err); \ + if (PGM_UNLIKELY(save_error)) { \ + pgm_error ("file %s: line %d (%s): error '%s' during '%s'", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, \ + strerror (save_error), name); \ + } \ + } while (0) +# define posix_check_cmd(cmd) posix_check_err ((cmd), #cmd) +#else +# define win32_check_err(err, name) \ + do { \ + const bool save_error = (err); \ + if (PGM_UNLIKELY(!save_error)) { \ + pgm_error ("file %s: line %d (%s): error '%s' during '%s'", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, \ + pgm_wsastrerror (GetLastError ()), name); \ + } \ + } while (0) +# define win32_check_cmd(cmd) win32_check_err ((cmd), #cmd) +#endif /* !_WIN32 */ + + +/* only needed for Win32 pre-Vista read-write locks + */ +void +pgm_thread_init (void) +{ + if (pgm_atomic_exchange_and_add32 (&thread_ref_count, 1) > 0) + return; + +#if defined(_WIN32) && !defined(CONFIG_HAVE_WIN_COND) + win32_check_cmd (TLS_OUT_OF_INDEXES != (cond_event_tls = TlsAlloc ())); +#endif +} + +void +pgm_thread_shutdown (void) +{ + pgm_return_if_fail (pgm_atomic_read32 (&thread_ref_count) > 0); + + if (pgm_atomic_exchange_and_add32 (&thread_ref_count, (uint32_t)-1) != 1) + return; + +#if defined(_WIN32) && !defined(CONFIG_HAVE_WIN_COND) + TlsFree (cond_event_tls); +#endif +} + +void +pgm_mutex_init ( + pgm_mutex_t* mutex + ) +{ + pgm_assert (NULL != mutex); +#ifndef _WIN32 + posix_check_cmd (pthread_mutex_init (&mutex->pthread_mutex, NULL)); +#else + HANDLE handle; + win32_check_cmd (handle = CreateMutex (NULL, FALSE, NULL)); + mutex->win32_mutex = handle; +#endif /* !_WIN32 */ +} + +bool +pgm_mutex_trylock ( + pgm_mutex_t* mutex + ) +{ + pgm_assert (NULL != mutex); +#ifndef _WIN32 + const int result = pthread_mutex_trylock (&mutex->pthread_mutex); + if (EBUSY == result) + return FALSE; + posix_check_err (result, "pthread_mutex_trylock"); + return TRUE; +#else + DWORD result; + win32_check_cmd (WAIT_FAILED != (result = WaitForSingleObject (mutex->win32_mutex, 0))); + return WAIT_TIMEOUT != result; +#endif /* !_WIN32 */ +} + +void +pgm_mutex_free ( + pgm_mutex_t* mutex + ) +{ + pgm_assert (NULL != mutex); +#ifndef _WIN32 + posix_check_cmd (pthread_mutex_destroy (&mutex->pthread_mutex)); +#else + win32_check_cmd (CloseHandle (mutex->win32_mutex)); +#endif /* !_WIN32 */ +} + +void +pgm_spinlock_init ( + pgm_spinlock_t* spinlock + ) +{ + pgm_assert (NULL != spinlock); +#ifndef _WIN32 + posix_check_cmd (pthread_spin_init (&spinlock->pthread_spinlock, PTHREAD_PROCESS_PRIVATE)); +#else + InitializeCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +bool +pgm_spinlock_trylock ( + pgm_spinlock_t* spinlock + ) +{ + pgm_assert (NULL != spinlock); +#ifndef _WIN32 + const int result = pthread_spin_trylock (&spinlock->pthread_spinlock); + if (EBUSY == result) + return FALSE; + posix_check_err (result, "pthread_spinlock_trylock"); + return TRUE; +#else + return TryEnterCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +void +pgm_spinlock_free ( + pgm_spinlock_t* spinlock + ) +{ + pgm_assert (NULL != spinlock); +#ifndef _WIN32 +/* ignore return value */ + pthread_spin_destroy (&spinlock->pthread_spinlock); +#else + DeleteCriticalSection (&spinlock->win32_spinlock); +#endif /* !_WIN32 */ +} + +void +pgm_cond_init ( + pgm_cond_t* cond + ) +{ + pgm_assert (NULL != cond); +#ifndef _WIN32 + posix_check_cmd (pthread_cond_init (&cond->pthread_cond, NULL)); +#elif defined(CONFIG_HAVE_WIN_COND) + InitializeConditionVariable (&cond->win32_cond); +#else + cond->len = 0; + cond->allocated_len = pgm_nearest_power (1, 2 + 1); + cond->phandle = pgm_new (HANDLE, cond->allocated_len); + InitializeCriticalSection (&cond->win32_spinlock); +#endif /* !_WIN32 */ +} + +void +pgm_cond_signal ( + pgm_cond_t* cond + ) +{ + pgm_assert (NULL != cond); +#ifndef _WIN32 + pthread_cond_signal (&cond->pthread_cond); +#elif defined(CONFIG_HAVE_WIN_COND) + WakeConditionVariable (&cond->win32_cond); +#else + EnterCriticalSection (&cond->win32_spinlock); + if (cond->len > 0) { + SetEvent (cond->phandle[ 0 ]); + memmove (&cond->phandle[ 0 ], &cond->phandle[ 1 ], cond->len - 1); + cond->len--; + } + LeaveCriticalSection (&cond->win32_spinlock); +#endif /* !_WIN32 */ +} + +void +pgm_cond_broadcast ( + pgm_cond_t* cond + ) +{ + pgm_assert (NULL != cond); +#ifndef _WIN32 + pthread_cond_broadcast (&cond->pthread_cond); +#elif defined(CONFIG_HAVE_WIN_COND) + WakeAllConditionVariable (&cond->win32_cond); +#else + EnterCriticalSection (&cond->win32_spinlock); + for (unsigned i = 0; i < cond->len; i++) + SetEvent (cond->phandle[ i ]); + cond->len = 0; + LeaveCriticalSection (&cond->win32_spinlock); +#endif /* !_WIN32 */ +} + +#ifndef _WIN32 +void +pgm_cond_wait ( + pgm_cond_t* cond, + pthread_mutex_t* mutex + ) +{ + pgm_assert (NULL != cond); + pgm_assert (NULL != mutex); + pthread_cond_wait (&cond->pthread_cond, mutex); +} +#else +void +pgm_cond_wait ( + pgm_cond_t* cond, + CRITICAL_SECTION* spinlock + ) +{ + pgm_assert (NULL != cond); + pgm_assert (NULL != spinlock); +# if defined(CONFIG_HAVE_WIN_COND) + SleepConditionVariableCS (&cond->win32_cond, spinlock, INFINITE); +# else + DWORD status; + HANDLE event = TlsGetValue (cond_event_tls); + + if (!event) { + win32_check_cmd (event = CreateEvent (0, FALSE, FALSE, NULL)); + TlsSetValue (cond_event_tls, event); + } + + EnterCriticalSection (&cond->win32_spinlock); + pgm_assert (WAIT_TIMEOUT == WaitForSingleObject (event, 0)); + if ((cond->len + 1) > cond->allocated_len) { + cond->allocated_len = pgm_nearest_power (1, cond->len + 1 + 1); + cond->phandle = pgm_realloc (cond->phandle, cond->allocated_len); + } + cond->phandle[ cond->len++ ] = event; + LeaveCriticalSection (&cond->win32_spinlock); + + EnterCriticalSection (spinlock); + win32_check_cmd (WAIT_FAILED != (status = WaitForSingleObject (event, INFINITE))); + LeaveCriticalSection (spinlock); + + if (WAIT_TIMEOUT == status) { + EnterCriticalSection (&cond->win32_spinlock); + for (unsigned i = 0; i < cond->len; i++) { + if (cond->phandle[ i ] == event) { + if (i != cond->len - 1) + memmove (&cond->phandle[ i ], &cond->phandle[ i + 1 ], sizeof(HANDLE) * (cond->len - i - 1)); + cond->len--; + break; + } + } + win32_check_cmd (WAIT_FAILED != (status = WaitForSingleObject (event, 0))); + LeaveCriticalSection (&cond->win32_spinlock); + } +# endif /* !CONFIG_HAVE_WIN_COND */ +} +#endif /* !_WIN32 */ + +void +pgm_cond_free ( + pgm_cond_t* cond + ) +{ + pgm_assert (NULL != cond); +#ifndef _WIN32 + posix_check_cmd (pthread_cond_destroy (&cond->pthread_cond)); +#elif defined(CONFIG_HAVE_WIN_COND) + /* nop */ +#else + DeleteCriticalSection (&cond->win32_spinlock); + pgm_free (cond->phandle); +#endif /* !_WIN32 */ +} + +void +pgm_rwlock_init ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); +#ifdef CONFIG_HAVE_WIN_SRW_LOCK + InitializeSRWLock (&rwlock->win32_lock); +#elif !defined(_WIN32) + posix_check_cmd (pthread_rwlock_init (&rwlock->pthread_rwlock, NULL)); +#else + InitializeCriticalSection (&rwlock->win32_spinlock); + pgm_cond_init (&rwlock->read_cond); + pgm_cond_init (&rwlock->write_cond); + rwlock->read_counter = 0; + rwlock->have_writer = FALSE; + rwlock->want_to_read = 0; + rwlock->want_to_write = 0; +#endif /* !CONFIG_HAVE_WIN_SRW_LOCK */ +} + +void +pgm_rwlock_free ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); +#ifdef CONFIG_HAVE_WIN_SRW_LOCK + /* nop */ +#elif !defined(_WIN32) + pthread_rwlock_destroy (&rwlock->pthread_rwlock); +#else + pgm_cond_free (&rwlock->read_cond); + pgm_cond_free (&rwlock->write_cond); + DeleteCriticalSection (&rwlock->win32_spinlock); +#endif /* !CONFIG_HAVE_WIN_SRW_LOCK */ +} + +#if !defined(CONFIG_HAVE_WIN_SRW_LOCK) && defined(_WIN32) +static inline +void +_pgm_rwlock_signal ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + if (rwlock->want_to_write) + pgm_cond_signal (&rwlock->write_cond); + else if (rwlock->want_to_read) + pgm_cond_broadcast (&rwlock->read_cond); +} + +void +pgm_rwlock_reader_lock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + EnterCriticalSection (&rwlock->win32_spinlock); + rwlock->want_to_read++; + while (rwlock->have_writer || rwlock->want_to_write) + pgm_cond_wait (&rwlock->read_cond, &rwlock->win32_spinlock); + rwlock->want_to_read--; + rwlock->read_counter++; + LeaveCriticalSection (&rwlock->win32_spinlock); +} + +bool +pgm_rwlock_reader_trylock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + bool status; + EnterCriticalSection (&rwlock->win32_spinlock); + if (!rwlock->have_writer && !rwlock->want_to_write) { + rwlock->read_counter++; + status = TRUE; + } else + status = FALSE; + LeaveCriticalSection (&rwlock->win32_spinlock); + return status; +} + +void +pgm_rwlock_reader_unlock( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + EnterCriticalSection (&rwlock->win32_spinlock); + rwlock->read_counter--; + if (rwlock->read_counter == 0) + _pgm_rwlock_signal (rwlock); + LeaveCriticalSection (&rwlock->win32_spinlock); +} + +void +pgm_rwlock_writer_lock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + EnterCriticalSection (&rwlock->win32_spinlock); + rwlock->want_to_write++; + while (rwlock->have_writer || rwlock->read_counter) + pgm_cond_wait (&rwlock->write_cond, &rwlock->win32_spinlock); + rwlock->want_to_write--; + rwlock->have_writer = TRUE; + LeaveCriticalSection (&rwlock->win32_spinlock); +} + +bool +pgm_rwlock_writer_trylock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + bool status; + EnterCriticalSection (&rwlock->win32_spinlock); + if (!rwlock->have_writer && !rwlock->read_counter) { + rwlock->have_writer = TRUE; + status = TRUE; + } else + status = FALSE; + LeaveCriticalSection (&rwlock->win32_spinlock); + return status; +} + +void +pgm_rwlock_writer_unlock ( + pgm_rwlock_t* rwlock + ) +{ + pgm_assert (NULL != rwlock); + EnterCriticalSection (&rwlock->win32_spinlock); + rwlock->have_writer = FALSE; + _pgm_rwlock_signal (rwlock); + LeaveCriticalSection (&rwlock->win32_spinlock); +} +#endif /* !_WIN32 && !CONFIG_HAVE_WIN_SRW_LOCK */ + + +/* eof */ |