| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| /* |
| ****************************************************************************** |
| * |
| * Copyright (C) 1997-2016, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| * |
| ****************************************************************************** |
| * |
| * File umutex.cpp |
| * |
| * Modification History: |
| * |
| * Date Name Description |
| * 04/02/97 aliu Creation. |
| * 04/07/99 srl updated |
| * 05/13/99 stephen Changed to umutex (from cmutex). |
| * 11/22/99 aliu Make non-global mutex autoinitialize [j151] |
| ****************************************************************************** |
| */ |
| |
| #include "umutex.h" |
| |
| #include "unicode/utypes.h" |
| #include "uassert.h" |
| #include "cmemory.h" |
| |
| |
| // 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. |
| #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( |
| (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); |
| } |
| } |
| } |
| |
| // 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); |
| } |
| |
| 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 |
| |
| //------------------------------------------------------------------------------------------- |
| // |
| // 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; |
| } |
| int sysErr = pthread_mutex_lock(&mutex->fMutex); |
| (void)sysErr; // Suppress unused variable warnings. |
| U_ASSERT(sysErr == 0); |
| } |
| |
| |
| 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) { |
| 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; |
| 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 |
| // 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_COMMON_API UBool U_EXPORT2 |
| umtx_initImplPreInit(UInitOnce &uio) { |
| pthread_mutex_lock(&initMutex); |
| int32_t state = uio.fState; |
| if (state == 0) { |
| umtx_storeRelease(uio.fState, 1); |
| pthread_mutex_unlock(&initMutex); |
| return TRUE; // Caller will next call the init function. |
| } else { |
| while (uio.fState == 1) { |
| // Another thread is currently running the initialization. |
| // Wait until it completes. |
| pthread_cond_wait(&initCondition, &initMutex); |
| } |
| pthread_mutex_unlock(&initMutex); |
| U_ASSERT(uio.fState == 2); |
| 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. |
| // Some threads may be racing to test the fState variable outside of the mutex, |
| // requiring the use of store/release when changing its value. |
| |
| 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); |
| } |
| |
| U_NAMESPACE_END |
| #endif |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Deprecated functions for setting user mutexes. |
| // |
| //-------------------------------------------------------------------------- |
| |
| U_DEPRECATED void U_EXPORT2 |
| u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *, |
| UMtxFn *, UMtxFn *, UErrorCode *status) { |
| if (U_SUCCESS(*status)) { |
| *status = U_UNSUPPORTED_ERROR; |
| } |
| return; |
| } |
| |
| |
| |
| U_DEPRECATED void U_EXPORT2 |
| u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, |
| UErrorCode *status) { |
| if (U_SUCCESS(*status)) { |
| *status = U_UNSUPPORTED_ERROR; |
| } |
| return; |
| } |