From 976c8a4387e5ca4d021924dcf22b9cb9c8c638a8 Mon Sep 17 00:00:00 2001 From: Hiroaki Nakamura Date: Fri, 5 Oct 2012 13:03:03 +0200 Subject: [PATCH] Add support for condition variables on all platforms --- include/uv-private/uv-unix.h | 1 + include/uv-private/uv-win.h | 21 +++ include/uv.h | 30 +++++ src/unix/thread.c | 112 ++++++++++++++++ src/win/thread.c | 245 ++++++++++++++++++++++++++++++++++- src/win/winapi.c | 20 +++ src/win/winapi.h | 24 ++++ test/test-condvar.c | 173 +++++++++++++++++++++++++ test/test-list.h | 10 ++ uv.gyp | 1 + 10 files changed, 636 insertions(+), 1 deletion(-) create mode 100644 test/test-condvar.c diff --git a/include/uv-private/uv-unix.h b/include/uv-private/uv-unix.h index 7f810a96..f50142c0 100644 --- a/include/uv-private/uv-unix.h +++ b/include/uv-private/uv-unix.h @@ -106,6 +106,7 @@ typedef pthread_t uv_thread_t; typedef pthread_mutex_t uv_mutex_t; typedef pthread_rwlock_t uv_rwlock_t; typedef UV_PLATFORM_SEM_T uv_sem_t; +typedef pthread_cond_t uv_cond_t; /* Platform-specific definitions for uv_spawn support. */ typedef gid_t uv_gid_t; diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index 132339ab..78dbb96b 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -171,6 +171,10 @@ typedef int (WSAAPI* LPFN_WSARECVFROM) typedef NTSTATUS *PNTSTATUS; #endif +#ifndef RTL_CONDITION_VARIABLE_INIT + typedef PVOID CONDITION_VARIABLE, *PCONDITION_VARIABLE; +#endif + typedef struct _AFD_POLL_HANDLE_INFO { HANDLE Handle; ULONG Events; @@ -208,6 +212,23 @@ typedef HANDLE uv_sem_t; typedef CRITICAL_SECTION uv_mutex_t; +/* This condition variable implementation is based on the SetEvent solution + * (section 3.2) at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html + * We could not use the SignalObjectAndWait solution (section 3.4) because + * it want the 2nd argument (type uv_mutex_t) of uv_cond_wait() and + * uv_cond_timedwait() to be HANDLEs, but we use CRITICAL_SECTIONs. + */ + +typedef union { + CONDITION_VARIABLE cond_var; + struct { + unsigned int waiters_count; + CRITICAL_SECTION waiters_count_lock; + HANDLE signal_event; + HANDLE broadcast_event; + } fallback; +} uv_cond_t; + typedef union { /* srwlock_ has type SRWLOCK, but not all toolchains define this type in */ /* windows.h. */ diff --git a/include/uv.h b/include/uv.h index 3dcd83e3..4badd788 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1790,6 +1790,36 @@ UV_EXTERN void uv_sem_post(uv_sem_t* sem); UV_EXTERN void uv_sem_wait(uv_sem_t* sem); UV_EXTERN int uv_sem_trywait(uv_sem_t* sem); +/* + * Same goes for the condition variable functions. + */ +UV_EXTERN int uv_cond_init(uv_cond_t* cond); +UV_EXTERN void uv_cond_destroy(uv_cond_t* cond); +UV_EXTERN void uv_cond_signal(uv_cond_t* cond); +UV_EXTERN void uv_cond_broadcast(uv_cond_t* cond); +/* Waits on a condition variable without a timeout. + * + * Note: + * 1. callers should be prepared to deal with spurious wakeups. + */ +UV_EXTERN void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex); +/* Waits on a condition variable with a timeout in nano seconds. + * Returns 0 for success or -1 on timeout, * aborts when other errors happen. + * + * Note: + * 1. callers should be prepared to deal with spurious wakeups. + * 2. the granularity of timeout on Windows is never less than one millisecond. + * 3. uv_cond_timedwait takes a relative timeout, not an absolute time. + * 4. the precision of timeout on OSX is never less than one microsecond. + * Here is the reason. + * OSX doesn't support CLOCK_MONOTONIC nor pthread_condattr_setclock() + * (see man pthread_cond_init on OSX). + * An example in man pthread_cond_timedwait on OSX uses gettimeofday() + * and its resolution is a microsecond. + */ +UV_EXTERN int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, + uint64_t timeout); + /* Runs a function once and only once. Concurrent calls to uv_once() with the * same guard will block all callers except one (it's unspecified which one). * The guard should be initialized statically with the UV_ONCE_INIT macro. diff --git a/src/unix/thread.c b/src/unix/thread.c index f85de1a4..f50961c9 100644 --- a/src/unix/thread.c +++ b/src/unix/thread.c @@ -26,6 +26,12 @@ #include #include +#if defined(__APPLE__) && defined(__MACH__) +#include +#endif /* defined(__APPLE__) && defined(__MACH__) */ + +#undef NANOSEC +#define NANOSEC ((uint64_t) 1e9) int uv_thread_join(uv_thread_t *tid) { if (pthread_join(*tid, NULL)) @@ -258,3 +264,109 @@ int uv_sem_trywait(uv_sem_t* sem) { } #endif /* defined(__APPLE__) && defined(__MACH__) */ + + +#if defined(__APPLE__) && defined(__MACH__) + +int uv_cond_init(uv_cond_t* cond) { + if (pthread_cond_init(cond, NULL)) + return -1; + else + return 0; +} + +#else /* !(defined(__APPLE__) && defined(__MACH__)) */ + +int uv_cond_init(uv_cond_t* cond) { + pthread_condattr_t attr; + + if (pthread_condattr_init(&attr)) + return -1; + + if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) + goto error2; + + if (pthread_cond_init(cond, &attr)) + goto error2; + + if (pthread_condattr_destroy(&attr)) + goto error; + + return 0; + +error: + pthread_cond_destroy(cond); +error2: + pthread_condattr_destroy(&attr); + return -1; +} + +#endif /* defined(__APPLE__) && defined(__MACH__) */ + +void uv_cond_destroy(uv_cond_t* cond) { + if (pthread_cond_destroy(cond)) + abort(); +} + +void uv_cond_signal(uv_cond_t* cond) { + if (pthread_cond_signal(cond)) + abort(); +} + +void uv_cond_broadcast(uv_cond_t* cond) { + if (pthread_cond_broadcast(cond)) + abort(); +} + +void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { + if (pthread_cond_wait(cond, mutex)) + abort(); +} + +#if defined(__APPLE__) && defined(__MACH__) + +int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { + int r; + struct timeval tv; + struct timespec ts; + uint64_t abstime; + + gettimeofday(&tv, NULL); + abstime = tv.tv_sec * 1e9 + tv.tv_usec * 1e3 + timeout; + ts.tv_sec = abstime / NANOSEC; + ts.tv_nsec = abstime % NANOSEC; + r = pthread_cond_timedwait(cond, mutex, &ts); + + if (r == 0) + return 0; + + if (r == ETIMEDOUT) + return -1; + + abort(); + return -1; /* Satisfy the compiler. */ +} + +#else /* !(defined(__APPLE__) && defined(__MACH__)) */ + +int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { + int r; + struct timespec ts; + uint64_t abstime; + + abstime = uv_hrtime() + timeout; + ts.tv_sec = abstime / NANOSEC; + ts.tv_nsec = abstime % NANOSEC; + r = pthread_cond_timedwait(cond, mutex, &ts); + + if (r == 0) + return 0; + + if (r == ETIMEDOUT) + return -1; + + abort(); + return -1; /* Satisfy the compiler. */ +} + +#endif /* defined(__APPLE__) && defined(__MACH__) */ diff --git a/src/win/thread.c b/src/win/thread.c index dad2b9fb..da6138ad 100644 --- a/src/win/thread.c +++ b/src/win/thread.c @@ -27,6 +27,7 @@ #define HAVE_SRWLOCK_API() (pTryAcquireSRWLockShared != NULL) +#define HAVE_CONDVAR_API() (pInitializeConditionVariable != NULL) #ifdef _MSC_VER /* msvc */ # define inline __inline @@ -56,6 +57,23 @@ inline static int uv__rwlock_fallback_trywrlock(uv_rwlock_t* rwlock); inline static void uv__rwlock_fallback_wrunlock(uv_rwlock_t* rwlock); +inline static int uv_cond_fallback_init(uv_cond_t* cond); +inline static void uv_cond_fallback_destroy(uv_cond_t* cond); +inline static void uv_cond_fallback_signal(uv_cond_t* cond); +inline static void uv_cond_fallback_broadcast(uv_cond_t* cond); +inline static void uv_cond_fallback_wait(uv_cond_t* cond, uv_mutex_t* mutex); +inline static int uv_cond_fallback_timedwait(uv_cond_t* cond, + uv_mutex_t* mutex, uint64_t timeout); + +inline static int uv_cond_condvar_init(uv_cond_t* cond); +inline static void uv_cond_condvar_destroy(uv_cond_t* cond); +inline static void uv_cond_condvar_signal(uv_cond_t* cond); +inline static void uv_cond_condvar_broadcast(uv_cond_t* cond); +inline static void uv_cond_condvar_wait(uv_cond_t* cond, uv_mutex_t* mutex); +inline static int uv_cond_condvar_timedwait(uv_cond_t* cond, + uv_mutex_t* mutex, uint64_t timeout); + + static NOINLINE void uv__once_inner(uv_once_t* guard, void (*callback)(void)) { DWORD result; @@ -81,7 +99,7 @@ static NOINLINE void uv__once_inner(uv_once_t* guard, } else { /* We lost the race. Destroy the event we created and wait for the */ - /* existing one to become signaled. */ + /* existing one todv become signaled. */ CloseHandle(created_event); result = WaitForSingleObject(existing_event, INFINITE); assert(result == WAIT_OBJECT_0); @@ -367,3 +385,228 @@ inline static int uv__rwlock_fallback_trywrlock(uv_rwlock_t* rwlock) { inline static void uv__rwlock_fallback_wrunlock(uv_rwlock_t* rwlock) { uv_mutex_unlock(&rwlock->fallback_.write_mutex_); } + + + +/* This condition variable implementation is based on the SetEvent solution + * (section 3.2) at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html + * We could not use the SignalObjectAndWait solution (section 3.4) because + * it want the 2nd argument (type uv_mutex_t) of uv_cond_wait() and + * uv_cond_timedwait() to be HANDLEs, but we use CRITICAL_SECTIONs. + */ + +inline static int uv_cond_fallback_init(uv_cond_t* cond) { + /* Initialize the count to 0. */ + cond->fallback.waiters_count = 0; + + InitializeCriticalSection(&cond->fallback.waiters_count_lock); + + /* Create an auto-reset event. */ + cond->fallback.signal_event = CreateEvent(NULL, /* no security */ + FALSE, /* auto-reset event */ + FALSE, /* non-signaled initially */ + NULL); /* unnamed */ + if (!cond->fallback.signal_event) + goto error2; + + /* Create a manual-reset event. */ + cond->fallback.broadcast_event = CreateEvent(NULL, /* no security */ + TRUE, /* manual-reset */ + FALSE, /* non-signaled */ + NULL); /* unnamed */ + if (!cond->fallback.broadcast_event) + goto error; + + return 0; + +error: + CloseHandle(cond->fallback.signal_event); +error2: + DeleteCriticalSection(&cond->fallback.waiters_count_lock); + return -1; +} + + +inline static int uv_cond_condvar_init(uv_cond_t* cond) { + pInitializeConditionVariable(&cond->cond_var); + return 0; +} + + +int uv_cond_init(uv_cond_t* cond) { + uv__once_init(); + + if (HAVE_CONDVAR_API()) + return uv_cond_condvar_init(cond); + else + return uv_cond_fallback_init(cond); +} + + +inline static void uv_cond_fallback_destroy(uv_cond_t* cond) { + if (!CloseHandle(cond->fallback.broadcast_event)) + abort(); + if (!CloseHandle(cond->fallback.signal_event)) + abort(); + DeleteCriticalSection(&cond->fallback.waiters_count_lock); +} + + +inline static void uv_cond_condvar_destroy(uv_cond_t* cond) { + /* nothing to do */ +} + + +void uv_cond_destroy(uv_cond_t* cond) { + if (HAVE_CONDVAR_API()) + uv_cond_condvar_destroy(cond); + else + uv_cond_fallback_destroy(cond); +} + + +inline static void uv_cond_fallback_signal(uv_cond_t* cond) { + int have_waiters; + + /* Avoid race conditions. */ + EnterCriticalSection(&cond->fallback.waiters_count_lock); + have_waiters = cond->fallback.waiters_count > 0; + LeaveCriticalSection(&cond->fallback.waiters_count_lock); + + if (have_waiters) + SetEvent(cond->fallback.signal_event); +} + + +inline static void uv_cond_condvar_signal(uv_cond_t* cond) { + pWakeConditionVariable(&cond->cond_var); +} + + +void uv_cond_signal(uv_cond_t* cond) { + if (HAVE_CONDVAR_API()) + uv_cond_condvar_signal(cond); + else + uv_cond_fallback_signal(cond); +} + + +inline static void uv_cond_fallback_broadcast(uv_cond_t* cond) { + int have_waiters; + + /* Avoid race conditions. */ + EnterCriticalSection(&cond->fallback.waiters_count_lock); + have_waiters = cond->fallback.waiters_count > 0; + LeaveCriticalSection(&cond->fallback.waiters_count_lock); + + if (have_waiters) + SetEvent(cond->fallback.broadcast_event); +} + + +inline static void uv_cond_condvar_broadcast(uv_cond_t* cond) { + pWakeAllConditionVariable(&cond->cond_var); +} + + +void uv_cond_broadcast(uv_cond_t* cond) { + if (HAVE_CONDVAR_API()) + uv_cond_condvar_broadcast(cond); + else + uv_cond_fallback_broadcast(cond); +} + + +inline int uv_cond_wait_helper(uv_cond_t* cond, uv_mutex_t* mutex, + DWORD dwMilliseconds) { + DWORD result; + int last_waiter; + HANDLE handles[2] = { + cond->fallback.signal_event, + cond->fallback.broadcast_event + }; + + /* Avoid race conditions. */ + EnterCriticalSection(&cond->fallback.waiters_count_lock); + cond->fallback.waiters_count++; + LeaveCriticalSection(&cond->fallback.waiters_count_lock); + + /* It's ok to release the here since Win32 manual-reset events */ + /* maintain state when used with . This avoids the "lost wakeup" */ + /* bug. */ + uv_mutex_unlock(mutex); + + /* Wait for either event to become signaled due to being */ + /* called or being called. */ + result = WaitForMultipleObjects(2, handles, FALSE, dwMilliseconds); + + EnterCriticalSection(&cond->fallback.waiters_count_lock); + cond->fallback.waiters_count--; + last_waiter = result == WAIT_OBJECT_0 + 1 + && cond->fallback.waiters_count == 0; + LeaveCriticalSection(&cond->fallback.waiters_count_lock); + + /* Some thread called . */ + if (last_waiter) { + /* We're the last waiter to be notified or to stop waiting, so reset the */ + /* the manual-reset event. */ + ResetEvent(cond->fallback.broadcast_event); + } + + /* Reacquire the . */ + uv_mutex_lock(mutex); + + if (result == WAIT_OBJECT_0 || result == WAIT_OBJECT_0 + 1) + return 0; + + if (result == WAIT_TIMEOUT) + return -1; + + abort(); + return -1; /* Satisfy the compiler. */ +} + + +inline static void uv_cond_fallback_wait(uv_cond_t* cond, uv_mutex_t* mutex) { + if (uv_cond_wait_helper(cond, mutex, INFINITE)) + abort(); +} + + +inline static void uv_cond_condvar_wait(uv_cond_t* cond, uv_mutex_t* mutex) { + if (!pSleepConditionVariableCS(&cond->cond_var, mutex, INFINITE)) + abort(); +} + + +void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { + if (HAVE_CONDVAR_API()) + uv_cond_condvar_wait(cond, mutex); + else + uv_cond_fallback_wait(cond, mutex); +} + + +inline static int uv_cond_fallback_timedwait(uv_cond_t* cond, + uv_mutex_t* mutex, uint64_t timeout) { + return uv_cond_wait_helper(cond, mutex, (DWORD)(timeout / 1e6)); +} + + +inline static int uv_cond_condvar_timedwait(uv_cond_t* cond, + uv_mutex_t* mutex, uint64_t timeout) { + if (pSleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6))) + return 0; + if (GetLastError() != ERROR_TIMEOUT) + abort(); + return -1; +} + + +int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, + uint64_t timeout) { + if (HAVE_CONDVAR_API()) + return uv_cond_condvar_timedwait(cond, mutex, timeout); + else + return uv_cond_fallback_timedwait(cond, mutex, timeout); +} diff --git a/src/win/winapi.c b/src/win/winapi.c index ab5513ab..ab68bba7 100644 --- a/src/win/winapi.c +++ b/src/win/winapi.c @@ -45,6 +45,11 @@ sTryAcquireSRWLockShared pTryAcquireSRWLockShared; sTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive; sReleaseSRWLockShared pReleaseSRWLockShared; sReleaseSRWLockExclusive pReleaseSRWLockExclusive; +sInitializeConditionVariable pInitializeConditionVariable; +sSleepConditionVariableCS pSleepConditionVariableCS; +sSleepConditionVariableSRW pSleepConditionVariableSRW; +sWakeAllConditionVariable pWakeAllConditionVariable; +sWakeConditionVariable pWakeConditionVariable; void uv_winapi_init() { @@ -129,4 +134,19 @@ void uv_winapi_init() { pReleaseSRWLockExclusive = (sReleaseSRWLockExclusive) GetProcAddress(kernel32_module, "ReleaseSRWLockExclusive"); + + pInitializeConditionVariable = (sInitializeConditionVariable) + GetProcAddress(kernel32_module, "InitializeConditionVariable"); + + pSleepConditionVariableCS = (sSleepConditionVariableCS) + GetProcAddress(kernel32_module, "SleepConditionVariableCS"); + + pSleepConditionVariableSRW = (sSleepConditionVariableSRW) + GetProcAddress(kernel32_module, "SleepConditionVariableSRW"); + + pWakeAllConditionVariable = (sWakeAllConditionVariable) + GetProcAddress(kernel32_module, "WakeAllConditionVariable"); + + pWakeConditionVariable = (sWakeConditionVariable) + GetProcAddress(kernel32_module, "WakeConditionVariable"); } diff --git a/src/win/winapi.h b/src/win/winapi.h index 1bf6304b..e023beed 100644 --- a/src/win/winapi.h +++ b/src/win/winapi.h @@ -4426,6 +4426,25 @@ typedef VOID (WINAPI* sReleaseSRWLockShared) typedef VOID (WINAPI* sReleaseSRWLockExclusive) (PSRWLOCK SRWLock); +typedef VOID (WINAPI* sInitializeConditionVariable) + (PCONDITION_VARIABLE ConditionVariable); + +typedef BOOL (WINAPI* sSleepConditionVariableCS) + (PCONDITION_VARIABLE ConditionVariable, + PCRITICAL_SECTION CriticalSection, + DWORD dwMilliseconds); + +typedef BOOL (WINAPI* sSleepConditionVariableSRW) + (PCONDITION_VARIABLE ConditionVariable, + PSRWLOCK SRWLock, + DWORD dwMilliseconds, + ULONG Flags); + +typedef VOID (WINAPI* sWakeAllConditionVariable) + (PCONDITION_VARIABLE ConditionVariable); + +typedef VOID (WINAPI* sWakeConditionVariable) + (PCONDITION_VARIABLE ConditionVariable); /* Ntdll function pointers */ @@ -4448,5 +4467,10 @@ extern sTryAcquireSRWLockShared pTryAcquireSRWLockShared; extern sTryAcquireSRWLockExclusive pTryAcquireSRWLockExclusive; extern sReleaseSRWLockShared pReleaseSRWLockShared; extern sReleaseSRWLockExclusive pReleaseSRWLockExclusive; +extern sInitializeConditionVariable pInitializeConditionVariable; +extern sSleepConditionVariableCS pSleepConditionVariableCS; +extern sSleepConditionVariableSRW pSleepConditionVariableSRW; +extern sWakeAllConditionVariable pWakeAllConditionVariable; +extern sWakeConditionVariable pWakeConditionVariable; #endif /* UV_WIN_WINAPI_H_ */ diff --git a/test/test-condvar.c b/test/test-condvar.c new file mode 100644 index 00000000..065dd4ec --- /dev/null +++ b/test/test-condvar.c @@ -0,0 +1,173 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include + +typedef struct { + uv_mutex_t mutex; + uv_cond_t cond; + int delay; + int use_broadcast; + volatile int posted; +} worker_config; + + +static void worker(void* arg) { + worker_config* c = arg; + + if (c->delay) + uv_sleep(c->delay); + + uv_mutex_lock(&c->mutex); + ASSERT(c->posted == 0); + c->posted = 1; + if (c->use_broadcast) + uv_cond_broadcast(&c->cond); + else + uv_cond_signal(&c->cond); + uv_mutex_unlock(&c->mutex); +} + + +TEST_IMPL(condvar_1) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + + ASSERT(0 == uv_cond_init(&wc.cond)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_mutex_lock(&wc.mutex); + uv_sleep(100); + uv_cond_wait(&wc.cond, &wc.mutex); + ASSERT(wc.posted == 1); + uv_mutex_unlock(&wc.mutex); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_cond_destroy(&wc.cond); + + return 0; +} + + +TEST_IMPL(condvar_2) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + wc.delay = 100; + + ASSERT(0 == uv_cond_init(&wc.cond)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_mutex_lock(&wc.mutex); + uv_cond_wait(&wc.cond, &wc.mutex); + uv_mutex_unlock(&wc.mutex); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_cond_destroy(&wc.cond); + + return 0; +} + + +TEST_IMPL(condvar_3) { + uv_thread_t thread; + worker_config wc; + int r; + + memset(&wc, 0, sizeof(wc)); + wc.delay = 100; + + ASSERT(0 == uv_cond_init(&wc.cond)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_mutex_lock(&wc.mutex); + r = uv_cond_timedwait(&wc.cond, &wc.mutex, (uint64_t)(50 * 1e6)); + ASSERT(r == -1); + uv_mutex_unlock(&wc.mutex); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_cond_destroy(&wc.cond); + + return 0; +} + + +TEST_IMPL(condvar_4) { + uv_thread_t thread; + worker_config wc; + int r; + + memset(&wc, 0, sizeof(wc)); + wc.delay = 100; + + ASSERT(0 == uv_cond_init(&wc.cond)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_mutex_lock(&wc.mutex); + r = uv_cond_timedwait(&wc.cond, &wc.mutex, (uint64_t)(150 * 1e6)); + ASSERT(r == 0); + uv_mutex_unlock(&wc.mutex); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_cond_destroy(&wc.cond); + + return 0; +} + + +TEST_IMPL(condvar_5) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + wc.use_broadcast = 1; + + ASSERT(0 == uv_cond_init(&wc.cond)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_mutex_lock(&wc.mutex); + uv_sleep(100); + uv_cond_wait(&wc.cond, &wc.mutex); + ASSERT(wc.posted == 1); + uv_mutex_unlock(&wc.mutex); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_cond_destroy(&wc.cond); + + return 0; +} diff --git a/test/test-list.h b/test/test-list.h index 53e11550..893be1a5 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -22,6 +22,11 @@ TEST_DECLARE (platform_output) TEST_DECLARE (callback_order) TEST_DECLARE (run_once) +TEST_DECLARE (condvar_1) +TEST_DECLARE (condvar_2) +TEST_DECLARE (condvar_3) +TEST_DECLARE (condvar_4) +TEST_DECLARE (condvar_5) TEST_DECLARE (semaphore_1) TEST_DECLARE (semaphore_2) TEST_DECLARE (semaphore_3) @@ -208,6 +213,11 @@ TASK_LIST_START TEST_ENTRY (callback_order) #endif TEST_ENTRY (run_once) + TEST_ENTRY (condvar_1) + TEST_ENTRY (condvar_2) + TEST_ENTRY (condvar_3) + TEST_ENTRY (condvar_4) + TEST_ENTRY (condvar_5) TEST_ENTRY (semaphore_1) TEST_ENTRY (semaphore_2) TEST_ENTRY (semaphore_3) diff --git a/uv.gyp b/uv.gyp index c9c6a58f..622b2346 100644 --- a/uv.gyp +++ b/uv.gyp @@ -305,6 +305,7 @@ 'test/test-mutexes.c', 'test/test-signal.c', 'test/test-thread.c', + 'test/test-condvar.c', 'test/test-timer-again.c', 'test/test-timer.c', 'test/test-tty.c',