diff options
Diffstat (limited to 'Build/source/libs/icu/icu-src/source/common/umutex.cpp')
-rw-r--r-- | Build/source/libs/icu/icu-src/source/common/umutex.cpp | 344 |
1 files changed, 85 insertions, 259 deletions
diff --git a/Build/source/libs/icu/icu-src/source/common/umutex.cpp b/Build/source/libs/icu/icu-src/source/common/umutex.cpp index 29dbc90ec98..ccbee9960a3 100644 --- a/Build/source/libs/icu/icu-src/source/common/umutex.cpp +++ b/Build/source/libs/icu/icu-src/source/common/umutex.cpp @@ -24,256 +24,142 @@ #include "unicode/utypes.h" #include "uassert.h" +#include "ucln_cmn.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_USES_ONLY_WIN32_API - -#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. +// Support for including an alternate implementation of mutexes has been withdrawn. +// See issue ICU-20185. +#error U_USER_MUTEX_CPP not supported #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. -// +/************************************************************************************************* + * + * ICU Mutex wrappers. + * + *************************************************************************************************/ -U_NAMESPACE_BEGIN +namespace { +std::mutex *initMutex; +std::condition_variable *initCondition; -U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) { - for (;;) { - int32_t previousState = InterlockedCompareExchange( - (LONG volatile *) // this is the type given in the API doc for this function. - &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); - } - } -} +// The ICU global mutex. +// Used when ICU implementation code passes nullptr for the mutex pointer. +UMutex globalMutex; -// This function is called by the thread that ran an initialization function, -// just after completing the function. +std::once_flag initFlag; +std::once_flag *pInitFlag = &initFlag; -U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) { - umtx_storeRelease(uio.fState, 2); -} +} // Anonymous namespace -U_NAMESPACE_END +U_CDECL_BEGIN +static UBool U_CALLCONV umtx_cleanup() { + initMutex->~mutex(); + initCondition->~condition_variable(); + UMutex::cleanup(); -static void winMutexInit(CRITICAL_SECTION *cs) { - InitializeCriticalSection(cs); - return; + // Reset the once_flag, by destructing it and creating a fresh one in its place. + // Do not use this trick anywhere else in ICU; use umtx_initOnce, not std::call_once(). + pInitFlag->~once_flag(); + pInitFlag = new(&initFlag) std::once_flag(); + return true; } -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); +static void U_CALLCONV umtx_init() { + initMutex = STATIC_NEW(std::mutex); + initCondition = STATIC_NEW(std::condition_variable); + ucln_common_registerCleanup(UCLN_COMMON_MUTEX, umtx_cleanup); } +U_CDECL_END -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; +std::mutex *UMutex::getMutex() { + std::mutex *retPtr = fMutex.load(std::memory_order_acquire); + if (retPtr == nullptr) { + std::call_once(*pInitFlag, umtx_init); + std::lock_guard<std::mutex> guard(*initMutex); + retPtr = fMutex.load(std::memory_order_acquire); + if (retPtr == nullptr) { + fMutex = new(fStorage) std::mutex(); + retPtr = fMutex; + fListLink = gListHead; + gListHead = this; + } } - ResetEvent(condition->fExitGate); - SetEvent(condition->fEntryGate); + U_ASSERT(retPtr != nullptr); + return retPtr; } -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); -} +UMutex *UMutex::gListHead = nullptr; -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); +void UMutex::cleanup() { + UMutex *next = nullptr; + for (UMutex *m = gListHead; m != nullptr; m = next) { + (*m->fMutex).~mutex(); + m->fMutex = nullptr; + next = m->fListLink; + m->fListLink = nullptr; } + gListHead = nullptr; } -#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) { + if (mutex == nullptr) { mutex = &globalMutex; } - int sysErr = pthread_mutex_lock(&mutex->fMutex); - (void)sysErr; // Suppress unused variable warnings. - U_ASSERT(sysErr == 0); + mutex->lock(); } U_CAPI void U_EXPORT2 umtx_unlock(UMutex* mutex) { - if (mutex == NULL) { - mutex = &globalMutex; - } - int sysErr = pthread_mutex_unlock(&mutex->fMutex); - (void)sysErr; // Suppress unused variable warnings. - U_ASSERT(sysErr == 0); -} - - -U_CAPI void U_EXPORT2 -umtx_condWait(UConditionVar *cond, UMutex *mutex) { - if (mutex == NULL) { + if (mutex == nullptr) { mutex = &globalMutex; } - int sysErr = pthread_cond_wait(&cond->fCondition, &mutex->fMutex); - (void)sysErr; - U_ASSERT(sysErr == 0); + mutex->unlock(); } -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; -static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER; +/************************************************************************************************* + * + * UInitOnce Implementation + * + *************************************************************************************************/ // 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::call_once(*pInitFlag, umtx_init); + 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. @@ -282,80 +168,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 *, |