ICU-10948 Condition Variables, merged from development branch.

X-SVN-Rev: 36101
This commit is contained in:
Andy Heninger 2014-07-29 23:18:47 +00:00
parent 26063a3454
commit 96835f80a5
4 changed files with 218 additions and 1 deletions

View file

@ -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;

View file

@ -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*/

View file

@ -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; i<NUMTHREADS; ++i) {
threads[i] = new CondThread;
threads[i]->start();
}
while (gStartedThreads < NUMTHREADS) {
umtx_condWait(&gCTConditionVar, &gCTMutex);
}
while (gFinishedThreads < NUMTHREADS) {
umtx_condWait(&gCTConditionVar, &gCTMutex);
}
umtx_unlock(&gCTMutex);
for (i=0; i<NUMTHREADS; ++i) {
if (!threads[i]->fFinished) {
errln("File %s, Line %d: Error, threads[%d]->fFinished == false", __FILE__, __LINE__, i);
}
delete threads[i];
}
}
#endif // ICU_USE_THREADS

View file

@ -47,6 +47,7 @@ public:
void TestCollators(void);
void TestString();
void TestAnyTranslit();
void TestConditionVariables();
};