]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/umutex.cpp
ICU-64252.0.1.tar.gz
[apple/icu.git] / icuSources / common / umutex.cpp
index 69eab79ff94508008cb1f056d0063b9026d4c387..774071eedb7bb966bd11d3f8ee01caa2cc201e32 100644 (file)
@@ -1,7 +1,9 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /*
 ******************************************************************************
 *
-*   Copyright (C) 1997-2015, International Business Machines
+*   Copyright (C) 1997-2016, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *
 ******************************************************************************
 #include "uassert.h"
 #include "cmemory.h"
 
+U_NAMESPACE_BEGIN
 
-// The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
-static UMutex   globalMutex = U_MUTEX_INITIALIZER;
-
-/*
- * ICU Mutex wrappers.  Wrap operating system mutexes, giving the rest of ICU a
- * platform independent set of mutex operations.  For internal ICU use only.
- */
 
 #if defined(U_USER_MUTEX_CPP)
-// Build time user mutex hook: #include "U_USER_MUTEX_CPP"
-#include U_MUTEX_XSTR(U_USER_MUTEX_CPP)
-
-#elif U_PLATFORM_HAS_WIN32_API
-
-//-------------------------------------------------------------------------------------------
-//
-//    Windows Specific Definitions
-//
-//        Note: Cygwin (and possibly others) have both WIN32 and POSIX.
-//              Prefer Win32 in these cases.  (Win32 comes ahead in the #if chain)
-//
-//-------------------------------------------------------------------------------------------
-
-#if defined U_NO_PLATFORM_ATOMICS
-#error ICU on Win32 requires support for low level atomic operations.
-// Visual Studio, gcc, clang are OK. Shouldn't get here.
-#endif
-
-
-// This function is called when a test of a UInitOnce::fState reveals that
-//   initialization has not completed, that we either need to call the
-//   function on this thread, or wait for some other thread to complete.
-//
-// The actual call to the init function is made inline by template code
-//   that knows the C++ types involved. This function returns TRUE if
-//   the caller needs to call the Init function.
-//
-
-U_NAMESPACE_BEGIN
-
-U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) {
-    for (;;) {
-        int32_t previousState = InterlockedCompareExchange(
-#if (U_PLATFORM == U_PF_MINGW) || (U_PLATFORM == U_PF_CYGWIN) || defined(__clang__)
-           (LONG volatile *) // this is the type given in the API doc for this function.
+// Support for including an alternate implementation of mutexes has been withdrawn.
+// See issue ICU-20185.
+#error U_USER_MUTEX_CPP not supported
 #endif
-            &uio.fState,  //  Destination
-            1,            //  Exchange Value
-            0);           //  Compare value
-
-        if (previousState == 0) {
-            return true;   // Caller will next call the init function.
-                           // Current state == 1.
-        } else if (previousState == 2) {
-            // Another thread already completed the initialization.
-            //   We can simply return FALSE, indicating no
-            //   further action is needed by the caller.
-            return FALSE;
-        } else {
-            // Another thread is currently running the initialization.
-            // Wait until it completes.
-            do {
-                Sleep(1);
-                previousState = umtx_loadAcquire(uio.fState);
-            } while (previousState == 1);
-        }
-    }
-}
-
-// This function is called by the thread that ran an initialization function,
-// just after completing the function.
-
-U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) {
-    umtx_storeRelease(uio.fState, 2);
-}
-
-U_NAMESPACE_END
-
-static void winMutexInit(CRITICAL_SECTION *cs) {
-    InitializeCriticalSection(cs);
-    return;
-}
-
-U_CAPI void  U_EXPORT2
-umtx_lock(UMutex *mutex) {
-    if (mutex == NULL) {
-        mutex = &globalMutex;
-    }
-    CRITICAL_SECTION *cs = &mutex->fCS;
-    umtx_initOnce(mutex->fInitOnce, winMutexInit, cs);
-    EnterCriticalSection(cs);
-}
 
-U_CAPI void  U_EXPORT2
-umtx_unlock(UMutex* mutex)
-{
-    if (mutex == NULL) {
-        mutex = &globalMutex;
-    }
-    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);
-}
+/*************************************************************************************************
+ *
+ *  ICU Mutex wrappers.
+ *
+ *************************************************************************************************/
 
-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);
-    }
+// The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
+static UMutex *globalMutex() {
+    static UMutex *m = STATIC_NEW(UMutex);
+    return m;
 }
 
-
-#elif U_PLATFORM_IMPLEMENTS_POSIX
-
-//-------------------------------------------------------------------------------------------
-//
-//  POSIX specific definitions
-//
-//-------------------------------------------------------------------------------------------
-
-# include <pthread.h>
-
-// Each UMutex consists of a pthread_mutex_t.
-// All are statically initialized and ready for use.
-// There is no runtime mutex initialization code needed.
-
 U_CAPI void  U_EXPORT2
 umtx_lock(UMutex *mutex) {
-    if (mutex == NULL) {
-        mutex = &globalMutex;
+    if (mutex == nullptr) {
+        mutex = globalMutex();
     }
-    int sysErr = pthread_mutex_lock(&mutex->fMutex);
-    (void)sysErr;   // Suppress unused variable warnings.
-    U_ASSERT(sysErr == 0);
+    mutex->fMutex.lock();
 }
 
 
 U_CAPI void  U_EXPORT2
 umtx_unlock(UMutex* mutex)
 {
-    if (mutex == NULL) {
-        mutex = &globalMutex;
+    if (mutex == nullptr) {
+        mutex = globalMutex();
     }
-    int sysErr = pthread_mutex_unlock(&mutex->fMutex);
-    (void)sysErr;   // Suppress unused variable warnings.
-    U_ASSERT(sysErr == 0);
+    mutex->fMutex.unlock();
 }
 
 
-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);
-}
+/*************************************************************************************************
+ *
+ *  UInitOnce Implementation
+ *
+ *************************************************************************************************/
 
-U_CAPI void U_EXPORT2
-umtx_condBroadcast(UConditionVar *cond) {
-    int sysErr = pthread_cond_broadcast(&cond->fCondition);
-    (void)sysErr;
-    U_ASSERT(sysErr == 0);
+static std::mutex &initMutex() {
+    static std::mutex *m = STATIC_NEW(std::mutex);
+    return *m;
 }
 
-U_CAPI void U_EXPORT2
-umtx_condSignal(UConditionVar *cond) {
-    int sysErr = pthread_cond_signal(&cond->fCondition);
-    (void)sysErr;
-    U_ASSERT(sysErr == 0);
+static std::condition_variable &initCondition() {
+    static std::condition_variable *cv = STATIC_NEW(std::condition_variable);
+    return *cv;
 }
 
 
-
-U_NAMESPACE_BEGIN
-
-static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER;
-
-
 // This function is called when a test of a UInitOnce::fState reveals that
-//   initialization has not completed, that we either need to call the
+//   initialization has not completed, that we either need to call the init
 //   function on this thread, or wait for some other thread to complete.
 //
 // The actual call to the init function is made inline by template code
-//   that knows the C++ types involved. This function returns TRUE if
+//   that knows the C++ types involved. This function returns true if
 //   the caller needs to call the Init function.
 //
 U_COMMON_API UBool U_EXPORT2
 umtx_initImplPreInit(UInitOnce &uio) {
-    pthread_mutex_lock(&initMutex);
-    int32_t state = uio.fState;
-    if (state == 0) {
+    std::unique_lock<std::mutex> lock(initMutex());
+
+    if (umtx_loadAcquire(uio.fState) == 0) {
         umtx_storeRelease(uio.fState, 1);
-        pthread_mutex_unlock(&initMutex);
-        return TRUE;   // Caller will next call the init function.
+        return true;      // Caller will next call the init function.
     } else {
-        while (uio.fState == 1) {
+        while (umtx_loadAcquire(uio.fState) == 1) {
             // Another thread is currently running the initialization.
             // Wait until it completes.
-            pthread_cond_wait(&initCondition, &initMutex);
+            initCondition().wait(lock);
         }
-        pthread_mutex_unlock(&initMutex);
         U_ASSERT(uio.fState == 2);
-        return FALSE;
+        return false;
     }
 }
 
 
-
 // This function is called by the thread that ran an initialization function,
 // just after completing the function.
 //   Some threads may be waiting on the condition, requiring the broadcast wakeup.
@@ -291,80 +118,20 @@ umtx_initImplPreInit(UInitOnce &uio) {
 
 U_COMMON_API void U_EXPORT2
 umtx_initImplPostInit(UInitOnce &uio) {
-    pthread_mutex_lock(&initMutex);
-    umtx_storeRelease(uio.fState, 2);
-    pthread_cond_broadcast(&initCondition);
-    pthread_mutex_unlock(&initMutex);
-}
-
-U_NAMESPACE_END
-
-// End of POSIX specific umutex implementation.
-
-#else  // Platform #define chain.
-
-#error Unknown Platform
-
-#endif  // Platform #define chain.
-
-
-//-------------------------------------------------------------------------------
-//
-//   Atomic Operations, out-of-line versions.
-//                      These are conditional, only defined if better versions
-//                      were not available for the platform.
-//
-//                      These versions are platform neutral.
-//
-//--------------------------------------------------------------------------------
-
-#if defined U_NO_PLATFORM_ATOMICS
-static UMutex   gIncDecMutex = U_MUTEX_INITIALIZER;
-
-U_NAMESPACE_BEGIN
-
-U_COMMON_API int32_t U_EXPORT2
-umtx_atomic_inc(u_atomic_int32_t *p)  {
-    int32_t retVal;
-    umtx_lock(&gIncDecMutex);
-    retVal = ++(*p);
-    umtx_unlock(&gIncDecMutex);
-    return retVal;
-}
-
-
-U_COMMON_API int32_t U_EXPORT2
-umtx_atomic_dec(u_atomic_int32_t *p) {
-    int32_t retVal;
-    umtx_lock(&gIncDecMutex);
-    retVal = --(*p);
-    umtx_unlock(&gIncDecMutex);
-    return retVal;
-}
-
-U_COMMON_API int32_t U_EXPORT2
-umtx_loadAcquire(u_atomic_int32_t &var) {
-    umtx_lock(&gIncDecMutex);
-    int32_t val = var;
-    umtx_unlock(&gIncDecMutex);
-    return val;
-}
-
-U_COMMON_API void U_EXPORT2
-umtx_storeRelease(u_atomic_int32_t &var, int32_t val) {
-    umtx_lock(&gIncDecMutex);
-    var = val;
-    umtx_unlock(&gIncDecMutex);
+    {
+        std::unique_lock<std::mutex> lock(initMutex());
+        umtx_storeRelease(uio.fState, 2);
+    }
+    initCondition().notify_all();
 }
 
 U_NAMESPACE_END
-#endif
 
-//--------------------------------------------------------------------------
-//
-//  Deprecated functions for setting user mutexes.
-//
-//--------------------------------------------------------------------------
+/*************************************************************************************************
+ *
+ *  Deprecated functions for setting user mutexes.
+ *
+ *************************************************************************************************/
 
 U_DEPRECATED void U_EXPORT2
 u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *,