]> granicus.if.org Git - icu/commitdiff
ICU-10948 Condition Variables, merged from development branch.
authorAndy Heninger <andy.heninger@gmail.com>
Tue, 29 Jul 2014 23:18:47 +0000 (23:18 +0000)
committerAndy Heninger <andy.heninger@gmail.com>
Tue, 29 Jul 2014 23:18:47 +0000 (23:18 +0000)
X-SVN-Rev: 36101

icu4c/source/common/umutex.cpp
icu4c/source/common/umutex.h
icu4c/source/test/intltest/tsmthred.cpp
icu4c/source/test/intltest/tsmthred.h

index c699e34c8f193673e585b21782b59319b36b87bb..0c1fdc8073005b89bdc92dd9feb6513c27757ceb 100644 (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;
index fe44cc0aeb331882a1ff12a3238dd706fcd1397c..e0ad0d3c03654d61a023a082548563d8d4c03c61 100644 (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*/
index da77b0f2ff89f2b09b2f97dbc40170fdde3fad6e..54d1fd9cacaa9a17cddd3205d36d87ee1813103c 100644 (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
index 76f80a174b3ebfa0bfade96fd8c4d119ce7ca1c5..a89f81814fd14c0626d64d530702391cefba9933 100644 (file)
@@ -47,6 +47,7 @@ public:
     void TestCollators(void);
     void TestString();
     void TestAnyTranslit();
+    void TestConditionVariables();
 
 };