// © 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 // 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; }