1 /* 2 ****************************************************************************** 3 * 4 * Copyright (C) 1997-2012, 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 "unicode/utypes.h" 22 #include "uassert.h" 23 #include "ucln_cmn.h" 24 25 /* 26 * ICU Mutex wrappers. Wrap operating system mutexes, giving the rest of ICU a 27 * platform independent set of mutex operations. For internal ICU use only. 28 */ 29 30 #if U_PLATFORM_HAS_WIN32_API 31 /* Prefer native Windows APIs even if POSIX is implemented (i.e., on Cygwin). */ 32 # undef POSIX 33 #elif U_PLATFORM_IMPLEMENTS_POSIX 34 # define POSIX 35 #else 36 # undef POSIX 37 #endif 38 39 #if defined(POSIX) 40 # include <pthread.h> /* must be first, so that we get the multithread versions of things. */ 41 #endif /* POSIX */ 42 43 #if U_PLATFORM_HAS_WIN32_API 44 # define WIN32_LEAN_AND_MEAN 45 # define VC_EXTRALEAN 46 # define NOUSER 47 # define NOSERVICE 48 # define NOIME 49 # define NOMCX 50 # include <windows.h> 51 #endif 52 53 #include "umutex.h" 54 #include "cmemory.h" 55 56 #if U_PLATFORM_HAS_WIN32_API 57 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ 58 InterlockedCompareExchangePointer(dest, newval, oldval) 59 60 #elif defined(POSIX) 61 #if (U_HAVE_GCC_ATOMICS == 1) 62 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ 63 __sync_val_compare_and_swap(dest, oldval, newval) 64 #else 65 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ 66 mutexed_compare_and_swap(dest, newval, oldval) 67 #endif 68 69 #else 70 // Unknown platform. Note that user can still set mutex functions at run time. 71 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ 72 mutexed_compare_and_swap(dest, newval, oldval) 73 #endif 74 75 static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval); 76 77 // The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer. 78 static UMutex globalMutex = U_MUTEX_INITIALIZER; 79 80 // Implementation mutex. Used for compare & swap when no intrinsic is available, and 81 // for safe initialization of user defined mutexes. 82 static UMutex implMutex = U_MUTEX_INITIALIZER; 83 84 // List of all user mutexes that have been initialized. 85 // Used to allow us to destroy them when cleaning up ICU. 86 // Normal platform mutexes are not kept track of in this way - they survive until the process is shut down. 87 // Normal platfrom mutexes don't allocate storage, so not cleaning them up won't trigger memory leak complaints. 88 // 89 // Note: putting this list in allocated memory would be awkward to arrange, because memory allocations 90 // are used as a flag to indicate that ICU has been initialized, and setting other ICU 91 // override functions will no longer work. 92 // 93 static const int MUTEX_LIST_LIMIT = 100; 94 static UMutex *gMutexList[MUTEX_LIST_LIMIT]; 95 static int gMutexListSize = 0; 96 97 98 /* 99 * User mutex implementation functions. If non-null, call back to these rather than 100 * directly using the system (Posix or Windows) APIs. See u_setMutexFunctions(). 101 * (declarations are in uclean.h) 102 */ 103 static UMtxInitFn *pMutexInitFn = NULL; 104 static UMtxFn *pMutexDestroyFn = NULL; 105 static UMtxFn *pMutexLockFn = NULL; 106 static UMtxFn *pMutexUnlockFn = NULL; 107 static const void *gMutexContext = NULL; 108 109 110 // Clean up (undo) the effects of u_setMutexFunctions(). 111 // usrMutexCleanup()112 static void usrMutexCleanup() { 113 if (pMutexDestroyFn != NULL) { 114 for (int i = 0; i < gMutexListSize; i++) { 115 UMutex *m = gMutexList[i]; 116 U_ASSERT(m->fInitialized); 117 (*pMutexDestroyFn)(gMutexContext, &m->fUserMutex); 118 m->fInitialized = FALSE; 119 } 120 (*pMutexDestroyFn)(gMutexContext, &globalMutex.fUserMutex); 121 (*pMutexDestroyFn)(gMutexContext, &implMutex.fUserMutex); 122 } 123 gMutexListSize = 0; 124 pMutexInitFn = NULL; 125 pMutexDestroyFn = NULL; 126 pMutexLockFn = NULL; 127 pMutexUnlockFn = NULL; 128 gMutexContext = NULL; 129 } 130 131 132 /* 133 * User mutex lock. 134 * 135 * User mutexes need to be initialized before they can be used. We use the impl mutex 136 * to synchronize the initialization check. This could be sped up on platforms that 137 * support alternate ways to safely check the initialization flag. 138 * 139 */ usrMutexLock(UMutex * mutex)140 static void usrMutexLock(UMutex *mutex) { 141 UErrorCode status = U_ZERO_ERROR; 142 if (!(mutex == &implMutex || mutex == &globalMutex)) { 143 umtx_lock(&implMutex); 144 if (!mutex->fInitialized) { 145 (*pMutexInitFn)(gMutexContext, &mutex->fUserMutex, &status); 146 U_ASSERT(U_SUCCESS(status)); 147 mutex->fInitialized = TRUE; 148 U_ASSERT(gMutexListSize < MUTEX_LIST_LIMIT); 149 if (gMutexListSize < MUTEX_LIST_LIMIT) { 150 gMutexList[gMutexListSize] = mutex; 151 ++gMutexListSize; 152 } 153 } 154 umtx_unlock(&implMutex); 155 } 156 (*pMutexLockFn)(gMutexContext, &mutex->fUserMutex); 157 } 158 159 160 161 #if defined(POSIX) 162 163 // 164 // POSIX implementation of UMutex. 165 // 166 // Each UMutex has a corresponding pthread_mutex_t. 167 // All are statically initialized and ready for use. 168 // There is no runtime mutex initialization code needed. 169 170 U_CAPI void U_EXPORT2 umtx_lock(UMutex * mutex)171 umtx_lock(UMutex *mutex) { 172 if (mutex == NULL) { 173 mutex = &globalMutex; 174 } 175 if (pMutexLockFn) { 176 usrMutexLock(mutex); 177 } else { 178 #if U_DEBUG 179 // #if to avoid unused variable warnings in non-debug builds. 180 int sysErr = pthread_mutex_lock(&mutex->fMutex); 181 U_ASSERT(sysErr == 0); 182 #else 183 pthread_mutex_lock(&mutex->fMutex); 184 #endif 185 } 186 } 187 188 189 U_CAPI void U_EXPORT2 umtx_unlock(UMutex * mutex)190 umtx_unlock(UMutex* mutex) 191 { 192 if (mutex == NULL) { 193 mutex = &globalMutex; 194 } 195 if (pMutexUnlockFn) { 196 (*pMutexUnlockFn)(gMutexContext, &mutex->fUserMutex); 197 } else { 198 #if U_DEBUG 199 // #if to avoid unused variable warnings in non-debug builds. 200 int sysErr = pthread_mutex_unlock(&mutex->fMutex); 201 U_ASSERT(sysErr == 0); 202 #else 203 pthread_mutex_unlock(&mutex->fMutex); 204 #endif 205 } 206 } 207 208 #elif U_PLATFORM_HAS_WIN32_API 209 // 210 // Windows implementation of UMutex. 211 // 212 // Each UMutex has a corresponding Windows CRITICAL_SECTION. 213 // CRITICAL_SECTIONS must be initialized before use. This is done 214 // with a InitOnceExcuteOnce operation. 215 // 216 // InitOnceExecuteOnce was introduced with Windows Vista. For now ICU 217 // must support Windows XP, so we roll our own. ICU will switch to the 218 // native Windows InitOnceExecuteOnce when possible. 219 220 typedef UBool (*U_PINIT_ONCE_FN) ( 221 U_INIT_ONCE *initOnce, 222 void *parameter, 223 void **context 224 ); 225 u_InitOnceExecuteOnce(U_INIT_ONCE * initOnce,U_PINIT_ONCE_FN initFn,void * parameter,void ** context)226 UBool u_InitOnceExecuteOnce( 227 U_INIT_ONCE *initOnce, 228 U_PINIT_ONCE_FN initFn, 229 void *parameter, 230 void **context) { 231 for (;;) { 232 long previousState = InterlockedCompareExchange( 233 &initOnce->fState, // Destination, 234 1, // Exchange Value 235 0); // Compare value 236 if (previousState == 2) { 237 // Initialization was already completed. 238 if (context != NULL) { 239 *context = initOnce->fContext; 240 } 241 return TRUE; 242 } 243 if (previousState == 1) { 244 // Initialization is in progress in some other thread. 245 // Loop until it completes. 246 Sleep(1); 247 continue; 248 } 249 250 // Initialization needed. Execute the callback function to do it. 251 U_ASSERT(previousState == 0); 252 U_ASSERT(initOnce->fState == 1); 253 UBool success = (*initFn)(initOnce, parameter, &initOnce->fContext); 254 U_ASSERT(success); // ICU is not supporting the failure case. 255 256 // Assign the state indicating that initialization has completed. 257 // Using InterlockedCompareExchange to do it ensures that all 258 // threads will have a consistent view of memory. 259 previousState = InterlockedCompareExchange(&initOnce->fState, 2, 1); 260 U_ASSERT(previousState == 1); 261 // Next loop iteration will see the initialization and return. 262 } 263 }; 264 winMutexInit(U_INIT_ONCE * initOnce,void * param,void ** context)265 static UBool winMutexInit(U_INIT_ONCE *initOnce, void *param, void **context) { 266 UMutex *mutex = static_cast<UMutex *>(param); 267 U_ASSERT(sizeof(CRITICAL_SECTION) <= sizeof(mutex->fCS)); 268 InitializeCriticalSection((CRITICAL_SECTION *)mutex->fCS); 269 return TRUE; 270 } 271 272 /* 273 * umtx_lock 274 */ 275 U_CAPI void U_EXPORT2 umtx_lock(UMutex * mutex)276 umtx_lock(UMutex *mutex) { 277 if (mutex == NULL) { 278 mutex = &globalMutex; 279 } 280 if (pMutexLockFn) { 281 usrMutexLock(mutex); 282 } else { 283 u_InitOnceExecuteOnce(&mutex->fInitOnce, winMutexInit, mutex, NULL); 284 EnterCriticalSection((CRITICAL_SECTION *)mutex->fCS); 285 } 286 } 287 288 U_CAPI void U_EXPORT2 umtx_unlock(UMutex * mutex)289 umtx_unlock(UMutex* mutex) 290 { 291 if (mutex == NULL) { 292 mutex = &globalMutex; 293 } 294 if (pMutexUnlockFn) { 295 (*pMutexUnlockFn)(gMutexContext, &mutex->fUserMutex); 296 } else { 297 LeaveCriticalSection((CRITICAL_SECTION *)mutex->fCS); 298 } 299 } 300 301 #endif // Windows Implementation 302 303 304 U_CAPI void U_EXPORT2 u_setMutexFunctions(const void * context,UMtxInitFn * i,UMtxFn * d,UMtxFn * l,UMtxFn * u,UErrorCode * status)305 u_setMutexFunctions(const void *context, UMtxInitFn *i, UMtxFn *d, UMtxFn *l, UMtxFn *u, 306 UErrorCode *status) { 307 if (U_FAILURE(*status)) { 308 return; 309 } 310 311 /* Can not set a mutex function to a NULL value */ 312 if (i==NULL || d==NULL || l==NULL || u==NULL) { 313 *status = U_ILLEGAL_ARGUMENT_ERROR; 314 return; 315 } 316 317 /* If ICU is not in an initial state, disallow this operation. */ 318 if (cmemory_inUse()) { 319 *status = U_INVALID_STATE_ERROR; 320 return; 321 } 322 323 // Clean up any previously set user mutex functions. 324 // It's possible to call u_setMutexFunctions() more than once without without explicitly cleaning up, 325 // and the last call should take. Kind of a corner case, but it worked once, there is a test for 326 // it, so we keep it working. The global and impl mutexes will have been created by the 327 // previous u_setMutexFunctions(), and now need to be destroyed. 328 329 usrMutexCleanup(); 330 331 /* Swap in the mutex function pointers. */ 332 pMutexInitFn = i; 333 pMutexDestroyFn = d; 334 pMutexLockFn = l; 335 pMutexUnlockFn = u; 336 gMutexContext = context; 337 gMutexListSize = 0; 338 339 /* Initialize the global and impl mutexes. Safe to do at this point because 340 * u_setMutexFunctions must be done in a single-threaded envioronment. Not thread safe. 341 */ 342 (*pMutexInitFn)(gMutexContext, &globalMutex.fUserMutex, status); 343 globalMutex.fInitialized = TRUE; 344 (*pMutexInitFn)(gMutexContext, &implMutex.fUserMutex, status); 345 implMutex.fInitialized = TRUE; 346 } 347 348 349 350 /* synchronized compare and swap function, for use when OS or compiler built-in 351 * equivalents aren't available. 352 */ mutexed_compare_and_swap(void ** dest,void * newval,void * oldval)353 static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval) { 354 umtx_lock(&implMutex); 355 void *temp = *dest; 356 if (temp == oldval) { 357 *dest = newval; 358 } 359 umtx_unlock(&implMutex); 360 361 return temp; 362 } 363 364 365 366 /*----------------------------------------------------------------- 367 * 368 * Atomic Increment and Decrement 369 * umtx_atomic_inc 370 * umtx_atomic_dec 371 * 372 *----------------------------------------------------------------*/ 373 374 /* Pointers to user-supplied inc/dec functions. Null if no funcs have been set. */ 375 static UMtxAtomicFn *pIncFn = NULL; 376 static UMtxAtomicFn *pDecFn = NULL; 377 static const void *gIncDecContext = NULL; 378 379 #if defined (POSIX) && (U_HAVE_GCC_ATOMICS == 0) 380 static UMutex gIncDecMutex = U_MUTEX_INITIALIZER; 381 #endif 382 383 U_CAPI int32_t U_EXPORT2 umtx_atomic_inc(int32_t * p)384 umtx_atomic_inc(int32_t *p) { 385 int32_t retVal; 386 if (pIncFn) { 387 retVal = (*pIncFn)(gIncDecContext, p); 388 } else { 389 #if U_PLATFORM_HAS_WIN32_API 390 retVal = InterlockedIncrement((LONG*)p); 391 #elif defined(USE_MAC_OS_ATOMIC_INCREMENT) 392 retVal = OSAtomicIncrement32Barrier(p); 393 #elif (U_HAVE_GCC_ATOMICS == 1) 394 retVal = __sync_add_and_fetch(p, 1); 395 #elif defined (POSIX) 396 umtx_lock(&gIncDecMutex); 397 retVal = ++(*p); 398 umtx_unlock(&gIncDecMutex); 399 #else 400 /* Unknown Platform. */ 401 retVal = ++(*p); 402 #endif 403 } 404 return retVal; 405 } 406 407 U_CAPI int32_t U_EXPORT2 umtx_atomic_dec(int32_t * p)408 umtx_atomic_dec(int32_t *p) { 409 int32_t retVal; 410 if (pDecFn) { 411 retVal = (*pDecFn)(gIncDecContext, p); 412 } else { 413 #if U_PLATFORM_HAS_WIN32_API 414 retVal = InterlockedDecrement((LONG*)p); 415 #elif defined(USE_MAC_OS_ATOMIC_INCREMENT) 416 retVal = OSAtomicDecrement32Barrier(p); 417 #elif (U_HAVE_GCC_ATOMICS == 1) 418 retVal = __sync_sub_and_fetch(p, 1); 419 #elif defined (POSIX) 420 umtx_lock(&gIncDecMutex); 421 retVal = --(*p); 422 umtx_unlock(&gIncDecMutex); 423 #else 424 /* Unknown Platform. */ 425 retVal = --(*p); 426 #endif 427 } 428 return retVal; 429 } 430 431 432 433 U_CAPI void U_EXPORT2 u_setAtomicIncDecFunctions(const void * context,UMtxAtomicFn * ip,UMtxAtomicFn * dp,UErrorCode * status)434 u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *ip, UMtxAtomicFn *dp, 435 UErrorCode *status) { 436 if (U_FAILURE(*status)) { 437 return; 438 } 439 /* Can not set a mutex function to a NULL value */ 440 if (ip==NULL || dp==NULL) { 441 *status = U_ILLEGAL_ARGUMENT_ERROR; 442 return; 443 } 444 /* If ICU is not in an initial state, disallow this operation. */ 445 if (cmemory_inUse()) { 446 *status = U_INVALID_STATE_ERROR; 447 return; 448 } 449 450 pIncFn = ip; 451 pDecFn = dp; 452 gIncDecContext = context; 453 454 #if U_DEBUG 455 { 456 int32_t testInt = 0; 457 U_ASSERT(umtx_atomic_inc(&testInt) == 1); /* Sanity Check. Do the functions work at all? */ 458 U_ASSERT(testInt == 1); 459 U_ASSERT(umtx_atomic_dec(&testInt) == 0); 460 U_ASSERT(testInt == 0); 461 } 462 #endif 463 } 464 465 466 /* 467 * Mutex Cleanup Function 468 * Reset the mutex function callback pointers. 469 * Called from the global ICU u_cleanup() function. 470 */ umtx_cleanup(void)471 U_CFUNC UBool umtx_cleanup(void) { 472 /* Extra, do-nothing function call to suppress compiler warnings on platforms where 473 * mutexed_compare_and_swap is not otherwise used. */ 474 void *pv = &globalMutex; 475 mutexed_compare_and_swap(&pv, NULL, NULL); 476 usrMutexCleanup(); 477 478 pIncFn = NULL; 479 pDecFn = NULL; 480 gIncDecContext = NULL; 481 482 return TRUE; 483 } 484