michael@0: /* michael@0: ****************************************************************************** michael@0: * michael@0: * Copyright (C) 1997-2013, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: * michael@0: ****************************************************************************** michael@0: * michael@0: * File umutex.cpp michael@0: * michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * 04/02/97 aliu Creation. michael@0: * 04/07/99 srl updated michael@0: * 05/13/99 stephen Changed to umutex (from cmutex). michael@0: * 11/22/99 aliu Make non-global mutex autoinitialize [j151] michael@0: ****************************************************************************** michael@0: */ michael@0: michael@0: #include "umutex.h" michael@0: michael@0: #include "unicode/utypes.h" michael@0: #include "uassert.h" michael@0: #include "cmemory.h" michael@0: #include "ucln_cmn.h" michael@0: michael@0: michael@0: // The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer. michael@0: static UMutex globalMutex = U_MUTEX_INITIALIZER; michael@0: michael@0: /* michael@0: * ICU Mutex wrappers. Wrap operating system mutexes, giving the rest of ICU a michael@0: * platform independent set of mutex operations. For internal ICU use only. michael@0: */ michael@0: michael@0: #if defined(U_USER_MUTEX_CPP) michael@0: // Build time user mutex hook: #include "U_USER_MUTEX_CPP" michael@0: #include U_MUTEX_XSTR(U_USER_MUTEX_CPP) michael@0: michael@0: #elif U_PLATFORM_HAS_WIN32_API michael@0: michael@0: //------------------------------------------------------------------------------------------- michael@0: // michael@0: // Windows Specific Definitions michael@0: // michael@0: // Note: Cygwin (and possibly others) have both WIN32 and POSIX. michael@0: // Prefer Win32 in these cases. (Win32 comes ahead in the #if chain) michael@0: // michael@0: //------------------------------------------------------------------------------------------- michael@0: michael@0: #if defined U_NO_PLATFORM_ATOMICS michael@0: #error ICU on Win32 requires support for low level atomic operations. michael@0: // Visual Studio, gcc, clang are OK. Shouldn't get here. michael@0: #endif michael@0: michael@0: michael@0: // This function is called when a test of a UInitOnce::fState reveals that michael@0: // initialization has not completed, that we either need to call the michael@0: // function on this thread, or wait for some other thread to complete. michael@0: // michael@0: // The actual call to the init function is made inline by template code michael@0: // that knows the C++ types involved. This function returns TRUE if michael@0: // the caller needs to call the Init function. michael@0: // michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) { michael@0: for (;;) { michael@0: int32_t previousState = InterlockedCompareExchange( michael@0: #if (U_PLATFORM == U_PF_MINGW) || (U_PLATFORM == U_PF_CYGWIN) michael@0: (LONG volatile *) // this is the type given in the API doc for this function. michael@0: #endif michael@0: &uio.fState, // Destination michael@0: 1, // Exchange Value michael@0: 0); // Compare value michael@0: michael@0: if (previousState == 0) { michael@0: return true; // Caller will next call the init function. michael@0: // Current state == 1. michael@0: } else if (previousState == 2) { michael@0: // Another thread already completed the initialization. michael@0: // We can simply return FALSE, indicating no michael@0: // further action is needed by the caller. michael@0: return FALSE; michael@0: } else { michael@0: // Another thread is currently running the initialization. michael@0: // Wait until it completes. michael@0: do { michael@0: Sleep(1); michael@0: previousState = umtx_loadAcquire(uio.fState); michael@0: } while (previousState == 1); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // This function is called by the thread that ran an initialization function, michael@0: // just after completing the function. michael@0: // michael@0: // success: True: the inialization succeeded. No further calls to the init michael@0: // function will be made. michael@0: // False: the initializtion failed. The next call to umtx_initOnce() michael@0: // will retry the initialization. michael@0: michael@0: U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) { michael@0: umtx_storeRelease(uio.fState, 2); michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: static void winMutexInit(CRITICAL_SECTION *cs) { michael@0: InitializeCriticalSection(cs); michael@0: return; michael@0: } michael@0: michael@0: U_CAPI void U_EXPORT2 michael@0: umtx_lock(UMutex *mutex) { michael@0: if (mutex == NULL) { michael@0: mutex = &globalMutex; michael@0: } michael@0: CRITICAL_SECTION *cs = &mutex->fCS; michael@0: umtx_initOnce(mutex->fInitOnce, winMutexInit, cs); michael@0: EnterCriticalSection(cs); michael@0: } michael@0: michael@0: U_CAPI void U_EXPORT2 michael@0: umtx_unlock(UMutex* mutex) michael@0: { michael@0: if (mutex == NULL) { michael@0: mutex = &globalMutex; michael@0: } michael@0: LeaveCriticalSection(&mutex->fCS); michael@0: } michael@0: michael@0: #elif U_PLATFORM_IMPLEMENTS_POSIX michael@0: michael@0: //------------------------------------------------------------------------------------------- michael@0: // michael@0: // POSIX specific definitions michael@0: // michael@0: //------------------------------------------------------------------------------------------- michael@0: michael@0: # include michael@0: michael@0: // Each UMutex consists of a pthread_mutex_t. michael@0: // All are statically initialized and ready for use. michael@0: // There is no runtime mutex initialization code needed. michael@0: michael@0: U_CAPI void U_EXPORT2 michael@0: umtx_lock(UMutex *mutex) { michael@0: if (mutex == NULL) { michael@0: mutex = &globalMutex; michael@0: } michael@0: int sysErr = pthread_mutex_lock(&mutex->fMutex); michael@0: (void)sysErr; // Suppress unused variable warnings. michael@0: U_ASSERT(sysErr == 0); michael@0: } michael@0: michael@0: michael@0: U_CAPI void U_EXPORT2 michael@0: umtx_unlock(UMutex* mutex) michael@0: { michael@0: if (mutex == NULL) { michael@0: mutex = &globalMutex; michael@0: } michael@0: int sysErr = pthread_mutex_unlock(&mutex->fMutex); michael@0: (void)sysErr; // Suppress unused variable warnings. michael@0: U_ASSERT(sysErr == 0); michael@0: } michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER; michael@0: static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER; michael@0: michael@0: michael@0: // This function is called when a test of a UInitOnce::fState reveals that michael@0: // initialization has not completed, that we either need to call the michael@0: // function on this thread, or wait for some other thread to complete. michael@0: // michael@0: // The actual call to the init function is made inline by template code michael@0: // that knows the C++ types involved. This function returns TRUE if michael@0: // the caller needs to call the Init function. michael@0: // michael@0: U_COMMON_API UBool U_EXPORT2 michael@0: umtx_initImplPreInit(UInitOnce &uio) { michael@0: pthread_mutex_lock(&initMutex); michael@0: int32_t state = uio.fState; michael@0: if (state == 0) { michael@0: umtx_storeRelease(uio.fState, 1); michael@0: pthread_mutex_unlock(&initMutex); michael@0: return TRUE; // Caller will next call the init function. michael@0: } else { michael@0: while (uio.fState == 1) { michael@0: // Another thread is currently running the initialization. michael@0: // Wait until it completes. michael@0: pthread_cond_wait(&initCondition, &initMutex); michael@0: } michael@0: pthread_mutex_unlock(&initMutex); michael@0: U_ASSERT(uio.fState == 2); michael@0: return FALSE; michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: // This function is called by the thread that ran an initialization function, michael@0: // just after completing the function. michael@0: // Some threads may be waiting on the condition, requiring the broadcast wakeup. michael@0: // Some threads may be racing to test the fState variable outside of the mutex, michael@0: // requiring the use of store/release when changing its value. michael@0: michael@0: U_COMMON_API void U_EXPORT2 michael@0: umtx_initImplPostInit(UInitOnce &uio) { michael@0: pthread_mutex_lock(&initMutex); michael@0: umtx_storeRelease(uio.fState, 2); michael@0: pthread_cond_broadcast(&initCondition); michael@0: pthread_mutex_unlock(&initMutex); michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: michael@0: // End of POSIX specific umutex implementation. michael@0: michael@0: #else // Platform #define chain. michael@0: michael@0: #error Unknown Platform michael@0: michael@0: #endif // Platform #define chain. michael@0: michael@0: michael@0: //------------------------------------------------------------------------------- michael@0: // michael@0: // Atomic Operations, out-of-line versions. michael@0: // These are conditional, only defined if better versions michael@0: // were not available for the platform. michael@0: // michael@0: // These versions are platform neutral. michael@0: // michael@0: //-------------------------------------------------------------------------------- michael@0: michael@0: #if defined U_NO_PLATFORM_ATOMICS michael@0: static UMutex gIncDecMutex = U_MUTEX_INITIALIZER; michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: michael@0: U_COMMON_API int32_t U_EXPORT2 michael@0: umtx_atomic_inc(u_atomic_int32_t *p) { michael@0: int32_t retVal; michael@0: umtx_lock(&gIncDecMutex); michael@0: retVal = ++(*p); michael@0: umtx_unlock(&gIncDecMutex); michael@0: return retVal; michael@0: } michael@0: michael@0: michael@0: U_COMMON_API int32_t U_EXPORT2 michael@0: umtx_atomic_dec(u_atomic_int32_t *p) { michael@0: int32_t retVal; michael@0: umtx_lock(&gIncDecMutex); michael@0: retVal = --(*p); michael@0: umtx_unlock(&gIncDecMutex); michael@0: return retVal; michael@0: } michael@0: michael@0: U_COMMON_API int32_t U_EXPORT2 michael@0: umtx_loadAcquire(u_atomic_int32_t &var) { michael@0: int32_t val = var; michael@0: umtx_lock(&gIncDecMutex); michael@0: umtx_unlock(&gIncDecMutex); michael@0: return val; michael@0: } michael@0: michael@0: U_COMMON_API void U_EXPORT2 michael@0: umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { michael@0: umtx_lock(&gIncDecMutex); michael@0: umtx_unlock(&gIncDecMutex); michael@0: var = val; michael@0: } michael@0: michael@0: U_NAMESPACE_END michael@0: #endif michael@0: michael@0: //-------------------------------------------------------------------------- michael@0: // michael@0: // Deprecated functions for setting user mutexes. michael@0: // michael@0: //-------------------------------------------------------------------------- michael@0: michael@0: U_DEPRECATED void U_EXPORT2 michael@0: u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *, michael@0: UMtxFn *, UMtxFn *, UErrorCode *status) { michael@0: if (U_SUCCESS(*status)) { michael@0: *status = U_UNSUPPORTED_ERROR; michael@0: } michael@0: return; michael@0: } michael@0: michael@0: michael@0: michael@0: U_DEPRECATED void U_EXPORT2 michael@0: u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, michael@0: UErrorCode *status) { michael@0: if (U_SUCCESS(*status)) { michael@0: *status = U_UNSUPPORTED_ERROR; michael@0: } michael@0: return; michael@0: }