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
//-------------------------------------------------------------------------------------------
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;
/*
**********************************************************************
-* Copyright (C) 1997-2013, International Business Machines
+* Copyright (C) 1997-2014, International Business Machines
* Corporation and others. All Rights Reserved.
**********************************************************************
*
// 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;
*/
#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
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
/*
*/
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*/
}
break;
+ case 7:
+ name = "TestConditionVariables";
+ if (exec) {
+ TestConditionVariables();
+ }
+ break;
default:
name = "";
break; //needed to end loop
#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
void TestCollators(void);
void TestString();
void TestAnyTranslit();
+ void TestConditionVariables();
};