diff --git a/icu4c/source/common/umutex.cpp b/icu4c/source/common/umutex.cpp index c699e34c8f1..0c1fdc80730 100644 --- a/icu4c/source/common/umutex.cpp +++ b/icu4c/source/common/umutex.cpp @@ -132,6 +132,63 @@ umtx_unlock(UMutex* mutex) LeaveCriticalSection(&mutex->fCS); } + +U_CAPI void U_EXPORT2 +umtx_condBroadcast(UConditionVar *condition) { + // We require that the associated mutex be held by the caller, + // so access to fWaitCount is protected and safe. No other thread can + // call condWait() while we are here. + if (condition->fWaitCount == 0) { + return; + } + ResetEvent(condition->fExitGate); + SetEvent(condition->fEntryGate); +} + +U_CAPI void U_EXPORT2 +umtx_condSignal(UConditionVar *condition) { + // Function not implemented. There is no immediate requirement from ICU to have it. + // Once ICU drops support for Windows XP and Server 2003, ICU Condition Variables will be + // changed to be thin wrappers on native Windows CONDITION_VARIABLEs, and this function + // becomes trivial to provide. + U_ASSERT(FALSE); +} + +U_CAPI void U_EXPORT2 +umtx_condWait(UConditionVar *condition, UMutex *mutex) { + if (condition->fEntryGate == NULL) { + // Note: because the associated mutex must be locked when calling + // wait, we know that there can not be multiple threads + // running here with the same condition variable. + // Meaning that lazy initialization is safe. + U_ASSERT(condition->fExitGate == NULL); + condition->fEntryGate = CreateEvent(NULL, // Security Attributes + TRUE, // Manual Reset + FALSE, // Initially reset + NULL); // Name. + U_ASSERT(condition->fEntryGate != NULL); + condition->fExitGate = CreateEvent(NULL, TRUE, TRUE, NULL); + U_ASSERT(condition->fExitGate != NULL); + } + + condition->fWaitCount++; + umtx_unlock(mutex); + WaitForSingleObject(condition->fEntryGate, INFINITE); + umtx_lock(mutex); + condition->fWaitCount--; + if (condition->fWaitCount == 0) { + // All threads that were waiting at the entry gate have woken up + // and moved through. Shut the entry gate and open the exit gate. + ResetEvent(condition->fEntryGate); + SetEvent(condition->fExitGate); + } else { + umtx_unlock(mutex); + WaitForSingleObject(condition->fExitGate, INFINITE); + umtx_lock(mutex); + } +} + + #elif U_PLATFORM_IMPLEMENTS_POSIX //------------------------------------------------------------------------------------------- @@ -168,6 +225,33 @@ umtx_unlock(UMutex* mutex) U_ASSERT(sysErr == 0); } + +U_CAPI void U_EXPORT2 +umtx_condWait(UConditionVar *cond, UMutex *mutex) { + if (mutex == NULL) { + mutex = &globalMutex; + } + int sysErr = pthread_cond_wait(&cond->fCondition, &mutex->fMutex); + (void)sysErr; + U_ASSERT(sysErr == 0); +} + +U_CAPI void U_EXPORT2 +umtx_condBroadcast(UConditionVar *cond) { + int sysErr = pthread_cond_broadcast(&cond->fCondition); + (void)sysErr; + U_ASSERT(sysErr == 0); +} + +U_CAPI void U_EXPORT2 +umtx_condSignal(UConditionVar *cond) { + int sysErr = pthread_cond_signal(&cond->fCondition); + (void)sysErr; + U_ASSERT(sysErr == 0); +} + + + U_NAMESPACE_BEGIN static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER; diff --git a/icu4c/source/common/umutex.h b/icu4c/source/common/umutex.h index fe44cc0aeb3..e0ad0d3c036 100644 --- a/icu4c/source/common/umutex.h +++ b/icu4c/source/common/umutex.h @@ -1,6 +1,6 @@ /* ********************************************************************** -* Copyright (C) 1997-2013, International Business Machines +* Copyright (C) 1997-2014, International Business Machines * Corporation and others. All Rights Reserved. ********************************************************************** * @@ -27,6 +27,7 @@ // Forward Declarations. UMutex is not in the ICU namespace (yet) because // there are some remaining references from plain C. struct UMutex; +struct UConditionVar; U_NAMESPACE_BEGIN struct UInitOnce; @@ -329,6 +330,14 @@ typedef struct UMutex { */ #define U_MUTEX_INITIALIZER {U_INITONCE_INITIALIZER} +struct UConditionVar { + HANDLE fEntryGate; + HANDLE fExitGate; + int32_t fWaitCount; +}; + +#define U_CONDITION_INITIALIZER {NULL, NULL, 0} + #elif U_PLATFORM_IMPLEMENTS_POSIX @@ -345,6 +354,11 @@ struct UMutex { typedef struct UMutex UMutex; #define U_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER} +struct UConditionVar { + pthread_cond_t fCondition; +}; +#define U_CONDITION_INITIALIZER {PTHREAD_COND_INITIALIZER} + #else /* @@ -379,5 +393,32 @@ U_INTERNAL void U_EXPORT2 umtx_lock(UMutex* mutex); */ U_INTERNAL void U_EXPORT2 umtx_unlock (UMutex* mutex); +/* + * Wait on a condition variable. + * The calling thread will unlock the mutex and wait on the condition variable. + * The mutex must be locked by the calling thread when invoking this function. + * + * @param cond the condition variable to wait on. + * @param mutex the associated mutex. + */ + +U_INTERNAL void U_EXPORT2 umtx_condWait(UConditionVar *cond, UMutex *mutex); + + +/* + * Broadcast wakeup of all threads waiting on a Condition. + * The associated mutex must be locked by the calling thread when calling + * this function; this is a temporary ICU restriction. + * + * @param cond the condition variable. + */ +U_INTERNAL void U_EXPORT2 umtx_condBroadcast(UConditionVar *cond); + +/* + * Signal a condition variable, waking up one waiting thread. + * CAUTION: Do not use. Place holder only. Not implemented for Windows. + */ +U_INTERNAL void U_EXPORT2 umtx_condSignal(UConditionVar *cond); + #endif /* UMUTEX_H */ /*eof*/ diff --git a/icu4c/source/test/intltest/tsmthred.cpp b/icu4c/source/test/intltest/tsmthred.cpp index da77b0f2ff8..54d1fd9caca 100644 --- a/icu4c/source/test/intltest/tsmthred.cpp +++ b/icu4c/source/test/intltest/tsmthred.cpp @@ -202,6 +202,12 @@ void MultithreadTest::runIndexedTest( int32_t index, UBool exec, } break; + case 7: + name = "TestConditionVariables"; + if (exec) { + TestConditionVariables(); + } + break; default: name = ""; break; //needed to end loop @@ -1601,4 +1607,89 @@ void MultithreadTest::TestAnyTranslit() { #endif // !UCONFIG_NO_TRANSLITERATION } + +// Condition Variables Test +// Create a swarm of threads. +// Using a mutex and a condition variables each thread +// Increments a global count of started threads. +// Broadcasts that it has started. +// Waits on the condition that all threads have started. +// Increments a global count of finished threads. +// Waits on the condition that all threads have finished. +// Exits. + +class CondThread: public SimpleThread { + public: + CondThread() :fFinished(false) {}; + ~CondThread() {}; + void run(); + bool fFinished; +}; + +static UMutex gCTMutex = U_MUTEX_INITIALIZER; +static UConditionVar gCTConditionVar = U_CONDITION_INITIALIZER; +int gConditionTestOne = 1; // Value one. Non-const, extern linkage to inhibit + // compiler assuming a known value. +int gStartedThreads; +int gFinishedThreads; +static const int NUMTHREADS = 10; + +static MultithreadTest *gThisTest = NULL; // Make test frame work functions available to + // non-member functions. + +// Worker thread function. +void CondThread::run() { + umtx_lock(&gCTMutex); + gStartedThreads += gConditionTestOne; + umtx_condBroadcast(&gCTConditionVar); + + while (gStartedThreads < NUMTHREADS) { + if (gFinishedThreads != 0) { + gThisTest->errln("File %s, Line %d: Error, gStartedThreads = %d, gFinishedThreads = %d", + __FILE__, __LINE__, gStartedThreads, gFinishedThreads); + } + umtx_condWait(&gCTConditionVar, &gCTMutex); + } + + gFinishedThreads += gConditionTestOne; + fFinished = true; + umtx_condBroadcast(&gCTConditionVar); + + while (gFinishedThreads < NUMTHREADS) { + umtx_condWait(&gCTConditionVar, &gCTMutex); + } + umtx_unlock(&gCTMutex); +} + +void MultithreadTest::TestConditionVariables() { + gThisTest = this; + gStartedThreads = 0; + gFinishedThreads = 0; + int i; + + umtx_lock(&gCTMutex); + CondThread *threads[NUMTHREADS]; + for (i=0; istart(); + } + + while (gStartedThreads < NUMTHREADS) { + umtx_condWait(&gCTConditionVar, &gCTMutex); + } + + while (gFinishedThreads < NUMTHREADS) { + umtx_condWait(&gCTConditionVar, &gCTMutex); + } + + umtx_unlock(&gCTMutex); + + for (i=0; ifFinished) { + errln("File %s, Line %d: Error, threads[%d]->fFinished == false", __FILE__, __LINE__, i); + } + delete threads[i]; + } +} + #endif // ICU_USE_THREADS diff --git a/icu4c/source/test/intltest/tsmthred.h b/icu4c/source/test/intltest/tsmthred.h index 76f80a174b3..a89f81814fd 100644 --- a/icu4c/source/test/intltest/tsmthred.h +++ b/icu4c/source/test/intltest/tsmthred.h @@ -47,6 +47,7 @@ public: void TestCollators(void); void TestString(); void TestAnyTranslit(); + void TestConditionVariables(); };