1/* 2****************************************************************************** 3* 4* Copyright (C) 1997-2013, International Business Machines 5* Corporation and others. All Rights Reserved. 6* 7****************************************************************************** 8* 9* File umutex.cpp 10* 11* Modification History: 12* 13* Date Name Description 14* 04/02/97 aliu Creation. 15* 04/07/99 srl updated 16* 05/13/99 stephen Changed to umutex (from cmutex). 17* 11/22/99 aliu Make non-global mutex autoinitialize [j151] 18****************************************************************************** 19*/ 20 21#include "umutex.h" 22 23#include "unicode/utypes.h" 24#include "uassert.h" 25#include "cmemory.h" 26#include "ucln_cmn.h" 27 28 29// The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer. 30static UMutex globalMutex = U_MUTEX_INITIALIZER; 31 32/* 33 * ICU Mutex wrappers. Wrap operating system mutexes, giving the rest of ICU a 34 * platform independent set of mutex operations. For internal ICU use only. 35 */ 36 37#if defined(U_USER_MUTEX_CPP) 38// Build time user mutex hook: #include "U_USER_MUTEX_CPP" 39#include U_MUTEX_XSTR(U_USER_MUTEX_CPP) 40 41#elif U_PLATFORM_HAS_WIN32_API 42 43//------------------------------------------------------------------------------------------- 44// 45// Windows Specific Definitions 46// 47// Note: Cygwin (and possibly others) have both WIN32 and POSIX. 48// Prefer Win32 in these cases. (Win32 comes ahead in the #if chain) 49// 50//------------------------------------------------------------------------------------------- 51 52#if defined U_NO_PLATFORM_ATOMICS 53#error ICU on Win32 requires support for low level atomic operations. 54// Visual Studio, gcc, clang are OK. Shouldn't get here. 55#endif 56 57 58// This function is called when a test of a UInitOnce::fState reveals that 59// initialization has not completed, that we either need to call the 60// function on this thread, or wait for some other thread to complete. 61// 62// The actual call to the init function is made inline by template code 63// that knows the C++ types involved. This function returns TRUE if 64// the caller needs to call the Init function. 65// 66 67U_NAMESPACE_BEGIN 68 69U_COMMON_API UBool U_EXPORT2 umtx_initImplPreInit(UInitOnce &uio) { 70 for (;;) { 71 int32_t previousState = InterlockedCompareExchange( 72#if (U_PLATFORM == U_PF_MINGW) || (U_PLATFORM == U_PF_CYGWIN) 73 (LONG volatile *) // this is the type given in the API doc for this function. 74#endif 75 &uio.fState, // Destination 76 1, // Exchange Value 77 0); // Compare value 78 79 if (previousState == 0) { 80 return true; // Caller will next call the init function. 81 // Current state == 1. 82 } else if (previousState == 2) { 83 // Another thread already completed the initialization. 84 // We can simply return FALSE, indicating no 85 // further action is needed by the caller. 86 return FALSE; 87 } else { 88 // Another thread is currently running the initialization. 89 // Wait until it completes. 90 do { 91 Sleep(1); 92 previousState = umtx_loadAcquire(uio.fState); 93 } while (previousState == 1); 94 } 95 } 96} 97 98// This function is called by the thread that ran an initialization function, 99// just after completing the function. 100// 101// success: True: the inialization succeeded. No further calls to the init 102// function will be made. 103// False: the initializtion failed. The next call to umtx_initOnce() 104// will retry the initialization. 105 106U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) { 107 umtx_storeRelease(uio.fState, 2); 108} 109 110U_NAMESPACE_END 111 112static void winMutexInit(CRITICAL_SECTION *cs) { 113 InitializeCriticalSection(cs); 114 return; 115} 116 117U_CAPI void U_EXPORT2 118umtx_lock(UMutex *mutex) { 119 if (mutex == NULL) { 120 mutex = &globalMutex; 121 } 122 CRITICAL_SECTION *cs = &mutex->fCS; 123 umtx_initOnce(mutex->fInitOnce, winMutexInit, cs); 124 EnterCriticalSection(cs); 125} 126 127U_CAPI void U_EXPORT2 128umtx_unlock(UMutex* mutex) 129{ 130 if (mutex == NULL) { 131 mutex = &globalMutex; 132 } 133 LeaveCriticalSection(&mutex->fCS); 134} 135 136#elif U_PLATFORM_IMPLEMENTS_POSIX 137 138//------------------------------------------------------------------------------------------- 139// 140// POSIX specific definitions 141// 142//------------------------------------------------------------------------------------------- 143 144# include <pthread.h> 145 146// Each UMutex consists of a pthread_mutex_t. 147// All are statically initialized and ready for use. 148// There is no runtime mutex initialization code needed. 149 150U_CAPI void U_EXPORT2 151umtx_lock(UMutex *mutex) { 152 if (mutex == NULL) { 153 mutex = &globalMutex; 154 } 155 int sysErr = pthread_mutex_lock(&mutex->fMutex); 156 (void)sysErr; // Suppress unused variable warnings. 157 U_ASSERT(sysErr == 0); 158} 159 160 161U_CAPI void U_EXPORT2 162umtx_unlock(UMutex* mutex) 163{ 164 if (mutex == NULL) { 165 mutex = &globalMutex; 166 } 167 int sysErr = pthread_mutex_unlock(&mutex->fMutex); 168 (void)sysErr; // Suppress unused variable warnings. 169 U_ASSERT(sysErr == 0); 170} 171 172U_NAMESPACE_BEGIN 173 174static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER; 175static pthread_cond_t initCondition = PTHREAD_COND_INITIALIZER; 176 177 178// This function is called when a test of a UInitOnce::fState reveals that 179// initialization has not completed, that we either need to call the 180// function on this thread, or wait for some other thread to complete. 181// 182// The actual call to the init function is made inline by template code 183// that knows the C++ types involved. This function returns TRUE if 184// the caller needs to call the Init function. 185// 186U_COMMON_API UBool U_EXPORT2 187umtx_initImplPreInit(UInitOnce &uio) { 188 pthread_mutex_lock(&initMutex); 189 int32_t state = uio.fState; 190 if (state == 0) { 191 umtx_storeRelease(uio.fState, 1); 192 pthread_mutex_unlock(&initMutex); 193 return TRUE; // Caller will next call the init function. 194 } else { 195 while (uio.fState == 1) { 196 // Another thread is currently running the initialization. 197 // Wait until it completes. 198 pthread_cond_wait(&initCondition, &initMutex); 199 } 200 pthread_mutex_unlock(&initMutex); 201 U_ASSERT(uio.fState == 2); 202 return FALSE; 203 } 204} 205 206 207 208// This function is called by the thread that ran an initialization function, 209// just after completing the function. 210// Some threads may be waiting on the condition, requiring the broadcast wakeup. 211// Some threads may be racing to test the fState variable outside of the mutex, 212// requiring the use of store/release when changing its value. 213 214U_COMMON_API void U_EXPORT2 215umtx_initImplPostInit(UInitOnce &uio) { 216 pthread_mutex_lock(&initMutex); 217 umtx_storeRelease(uio.fState, 2); 218 pthread_cond_broadcast(&initCondition); 219 pthread_mutex_unlock(&initMutex); 220} 221 222U_NAMESPACE_END 223 224// End of POSIX specific umutex implementation. 225 226#else // Platform #define chain. 227 228#error Unknown Platform 229 230#endif // Platform #define chain. 231 232 233//------------------------------------------------------------------------------- 234// 235// Atomic Operations, out-of-line versions. 236// These are conditional, only defined if better versions 237// were not available for the platform. 238// 239// These versions are platform neutral. 240// 241//-------------------------------------------------------------------------------- 242 243#if defined U_NO_PLATFORM_ATOMICS 244static UMutex gIncDecMutex = U_MUTEX_INITIALIZER; 245 246U_NAMESPACE_BEGIN 247 248U_COMMON_API int32_t U_EXPORT2 249umtx_atomic_inc(u_atomic_int32_t *p) { 250 int32_t retVal; 251 umtx_lock(&gIncDecMutex); 252 retVal = ++(*p); 253 umtx_unlock(&gIncDecMutex); 254 return retVal; 255} 256 257 258U_COMMON_API int32_t U_EXPORT2 259umtx_atomic_dec(u_atomic_int32_t *p) { 260 int32_t retVal; 261 umtx_lock(&gIncDecMutex); 262 retVal = --(*p); 263 umtx_unlock(&gIncDecMutex); 264 return retVal; 265} 266 267U_COMMON_API int32_t U_EXPORT2 268umtx_loadAcquire(u_atomic_int32_t &var) { 269 int32_t val = var; 270 umtx_lock(&gIncDecMutex); 271 umtx_unlock(&gIncDecMutex); 272 return val; 273} 274 275U_COMMON_API void U_EXPORT2 276umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 277 umtx_lock(&gIncDecMutex); 278 umtx_unlock(&gIncDecMutex); 279 var = val; 280} 281 282U_NAMESPACE_END 283#endif 284 285//-------------------------------------------------------------------------- 286// 287// Deprecated functions for setting user mutexes. 288// 289//-------------------------------------------------------------------------- 290 291U_DEPRECATED void U_EXPORT2 292u_setMutexFunctions(const void * /*context */, UMtxInitFn *, UMtxFn *, 293 UMtxFn *, UMtxFn *, UErrorCode *status) { 294 if (U_SUCCESS(*status)) { 295 *status = U_UNSUPPORTED_ERROR; 296 } 297 return; 298} 299 300 301 302U_DEPRECATED void U_EXPORT2 303u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, 304 UErrorCode *status) { 305 if (U_SUCCESS(*status)) { 306 *status = U_UNSUPPORTED_ERROR; 307 } 308 return; 309} 310