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. 30 static 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 67 U_NAMESPACE_BEGIN 68 69 U_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 106 U_COMMON_API void U_EXPORT2 umtx_initImplPostInit(UInitOnce &uio) { 107 umtx_storeRelease(uio.fState, 2); 108 } 109 110 U_NAMESPACE_END 111 112 static void winMutexInit(CRITICAL_SECTION *cs) { 113 InitializeCriticalSection(cs); 114 return; 115 } 116 117 U_CAPI void U_EXPORT2 118 umtx_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 127 U_CAPI void U_EXPORT2 128 umtx_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 150 U_CAPI void U_EXPORT2 151 umtx_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 161 U_CAPI void U_EXPORT2 162 umtx_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 172 U_NAMESPACE_BEGIN 173 174 static pthread_mutex_t initMutex = PTHREAD_MUTEX_INITIALIZER; 175 static 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 // 186 U_COMMON_API UBool U_EXPORT2 187 umtx_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 214 U_COMMON_API void U_EXPORT2 215 umtx_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 222 U_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 244 static UMutex gIncDecMutex = U_MUTEX_INITIALIZER; 245 246 U_NAMESPACE_BEGIN 247 248 U_COMMON_API int32_t U_EXPORT2 249 umtx_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 258 U_COMMON_API int32_t U_EXPORT2 259 umtx_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 267 U_COMMON_API int32_t U_EXPORT2 268 umtx_loadAcquire(u_atomic_int32_t &var) { 269 int32_t val = var; 270 umtx_lock(&gIncDecMutex); 271 umtx_unlock(&gIncDecMutex); 272 return val; 273 } 274 275 U_COMMON_API void U_EXPORT2 276 umtx_storeRelease(u_atomic_int32_t &var, int32_t val) { 277 umtx_lock(&gIncDecMutex); 278 umtx_unlock(&gIncDecMutex); 279 var = val; 280 } 281 282 U_NAMESPACE_END 283 #endif 284 285 //-------------------------------------------------------------------------- 286 // 287 // Deprecated functions for setting user mutexes. 288 // 289 //-------------------------------------------------------------------------- 290 291 U_DEPRECATED void U_EXPORT2 292 u_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 302 U_DEPRECATED void U_EXPORT2 303 u_setAtomicIncDecFunctions(const void * /*context */, UMtxAtomicFn *, UMtxAtomicFn *, 304 UErrorCode *status) { 305 if (U_SUCCESS(*status)) { 306 *status = U_UNSUPPORTED_ERROR; 307 } 308 return; 309 } 310