1 /* 2 ****************************************************************************** 3 * 4 * Copyright (C) 1997-2009, International Business Machines 5 * Corporation and others. All Rights Reserved. 6 * 7 ****************************************************************************** 8 * 9 * File umutex.c 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 defined(U_DARWIN) 31 #include <AvailabilityMacros.h> 32 #if (ICU_USE_THREADS == 1) && defined(MAC_OS_X_VERSION_10_4) && defined(MAC_OS_X_VERSION_MIN_REQUIRED) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) 33 #if defined(__STRICT_ANSI__) 34 #define UPRV_REMAP_INLINE 35 #define inline 36 #endif 37 #include <libkern/OSAtomic.h> 38 #define USE_MAC_OS_ATOMIC_INCREMENT 1 39 #if defined(UPRV_REMAP_INLINE) 40 #undef inline 41 #undef UPRV_REMAP_INLINE 42 #endif 43 #endif 44 #endif 45 46 /* Assume POSIX, and modify as necessary below */ 47 #define POSIX 48 49 #if defined(U_WINDOWS) 50 #undef POSIX 51 #endif 52 #if defined(macintosh) 53 #undef POSIX 54 #endif 55 #if defined(OS2) 56 #undef POSIX 57 #endif 58 59 #if defined(POSIX) && (ICU_USE_THREADS==1) 60 # include <pthread.h> /* must be first, so that we get the multithread versions of things. */ 61 62 #endif /* POSIX && (ICU_USE_THREADS==1) */ 63 64 #ifdef U_WINDOWS 65 # define WIN32_LEAN_AND_MEAN 66 # define VC_EXTRALEAN 67 # define NOUSER 68 # define NOSERVICE 69 # define NOIME 70 # define NOMCX 71 # include <windows.h> 72 #endif 73 74 #include "umutex.h" 75 #include "cmemory.h" 76 77 /* 78 * A note on ICU Mutex Initialization and ICU startup: 79 * 80 * ICU mutexes, as used through the rest of the ICU code, are self-initializing. 81 * To make this work, ICU uses the _ICU GLobal Mutex_ to synchronize the lazy init 82 * of other ICU mutexes. For the global mutex itself, we need some other mechanism 83 * to safely initialize it on first use. This becomes important when two or more 84 * threads are more or less simultaenously the first to use ICU in a process, and 85 * are racing into the mutex initialization code. 86 * 87 * 88 * The solution for the global mutex init is platform dependent. 89 * On POSIX systems, plain C-style initialization can be used on a mutex, with the 90 * macro PTHREAD_MUTEX_INITIALIZER. The mutex is then ready for use, without 91 * first calling pthread_mutex_init(). 92 * 93 * Windows has no equivalent statically initialized mutex or CRITICAL SECION. 94 * InitializeCriticalSection() must be called. If the global mutex does not 95 * appear to be initialized, a thread will create and initialize a new 96 * CRITICAL_SECTION, then use a Windows InterlockedCompareAndExchange to 97 * swap it in as the global mutex while avoid problems with race conditions. 98 */ 99 100 /* On WIN32 mutexes are reentrant. On POSIX platforms they are not, and a deadlock 101 * will occur if a thread attempts to acquire a mutex it already has locked. 102 * ICU mutexes (in debug builds) include checking code that will cause an assertion 103 * failure if a mutex is reentered. If you are having deadlock problems 104 * on a POSIX machine, debugging may be easier on Windows. 105 */ 106 107 108 #if (ICU_USE_THREADS == 0) 109 #define MUTEX_TYPE void * 110 #define PLATFORM_MUTEX_INIT(m) 111 #define PLATFORM_MUTEX_LOCK(m) 112 #define PLATFORM_MUTEX_UNLOCK(m) 113 #define PLATFORM_MUTEX_DESTROY(m) 114 #define PLATFORM_MUTEX_INITIALIZER NULL 115 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ 116 mutexed_compare_and_swap(dest, newval, oldval) 117 118 119 #elif defined(U_WINDOWS) 120 #define MUTEX_TYPE CRITICAL_SECTION 121 #define PLATFORM_MUTEX_INIT(m) InitializeCriticalSection(m) 122 #define PLATFORM_MUTEX_LOCK(m) EnterCriticalSection(m) 123 #define PLATFORM_MUTEX_UNLOCK(m) LeaveCriticalSection(m) 124 #define PLATFORM_MUTEX_DESTROY(m) DeleteCriticalSection(m) 125 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ 126 InterlockedCompareExchangePointer(dest, newval, oldval) 127 128 129 #elif defined(POSIX) 130 #define MUTEX_TYPE pthread_mutex_t 131 #define PLATFORM_MUTEX_INIT(m) pthread_mutex_init(m, NULL) 132 #define PLATFORM_MUTEX_LOCK(m) pthread_mutex_lock(m) 133 #define PLATFORM_MUTEX_UNLOCK(m) pthread_mutex_unlock(m) 134 #define PLATFORM_MUTEX_DESTROY(m) pthread_mutex_destroy(m) 135 #define PLATFORM_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER 136 #if (U_HAVE_GCC_ATOMICS == 1) 137 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ 138 __sync_val_compare_and_swap(dest, oldval, newval) 139 #else 140 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ 141 mutexed_compare_and_swap(dest, newval, oldval) 142 #endif 143 144 145 #else 146 /* Unknown platform. Note that user can still set mutex functions at run time. */ 147 #define MUTEX_TYPE void * 148 #define PLATFORM_MUTEX_INIT(m) 149 #define PLATFORM_MUTEX_LOCK(m) 150 #define PLATFORM_MUTEX_UNLOCK(m) 151 #define PLATFORM_MUTEX_DESTROY(m) 152 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ 153 mutexed_compare_and_swap(dest, newval, oldval) 154 155 #endif 156 157 /* Forward declarations */ 158 static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval); 159 typedef struct ICUMutex ICUMutex; 160 161 /* 162 * ICUMutex One of these is set up for each UMTX that is used by other ICU code. 163 * The opaque UMTX points to the corresponding ICUMutex struct. 164 * 165 * Because the total number of ICU mutexes is quite small, no effort has 166 * been made to squeeze every byte out of this struct. 167 */ 168 struct ICUMutex { 169 UMTX *owner; /* Points back to the UMTX corrsponding to this */ 170 /* ICUMutex object. */ 171 172 UBool heapAllocated; /* Set if this ICUMutex is heap allocated, and */ 173 /* will need to be deleted. The global mutex */ 174 /* is static on POSIX platforms; all others */ 175 /* will be heap allocated. */ 176 177 ICUMutex *next; /* All ICUMutexes are chained into a list so that */ 178 /* they can be found and deleted by u_cleanup(). */ 179 180 int32_t recursionCount; /* For debugging, detect recursive mutex locks. */ 181 182 MUTEX_TYPE platformMutex; /* The underlying OS mutex being wrapped. */ 183 184 UMTX userMutex; /* For use with u_setMutexFunctions operations, */ 185 /* corresponds to platformMutex. */ 186 }; 187 188 189 /* The global ICU mutex. 190 * For POSIX platforms, it gets a C style initialization, and is ready to use 191 * at program startup. 192 * For Windows, it will be lazily instantiated on first use. 193 */ 194 195 #if defined(POSIX) 196 static UMTX globalUMTX; 197 static ICUMutex globalMutex = {&globalUMTX, FALSE, NULL, 0, PLATFORM_MUTEX_INITIALIZER, NULL}; 198 static UMTX globalUMTX = &globalMutex; 199 #else 200 static UMTX globalUMTX = NULL; 201 #endif 202 203 /* Head of the list of all ICU mutexes. 204 * Linked list is through ICUMutex::next 205 * Modifications to the list are synchronized with the global mutex. 206 * The list is used by u_cleanup(), which needs to dispose of all of the ICU mutexes. 207 * 208 * The statically initialized global mutex on POSIX platforms does not get added to this 209 * mutex list, but that's not a problem - the global mutex gets special handling 210 * during u_cleanup(). 211 */ 212 static ICUMutex *mutexListHead; 213 214 215 /* 216 * User mutex implementation functions. If non-null, call back to these rather than 217 * directly using the system (Posix or Windows) APIs. See u_setMutexFunctions(). 218 * (declarations are in uclean.h) 219 */ 220 static UMtxInitFn *pMutexInitFn = NULL; 221 static UMtxFn *pMutexDestroyFn = NULL; 222 static UMtxFn *pMutexLockFn = NULL; 223 static UMtxFn *pMutexUnlockFn = NULL; 224 static const void *gMutexContext = NULL; 225 226 227 /* 228 * umtx_lock 229 */ 230 U_CAPI void U_EXPORT2 231 umtx_lock(UMTX *mutex) 232 { 233 ICUMutex *m; 234 235 if (mutex == NULL) { 236 mutex = &globalUMTX; 237 } 238 m = (ICUMutex *)*mutex; 239 if (m == NULL) { 240 /* See note on lazy initialization, above. We can get away with it here, with mutexes, 241 * where we couldn't with normal user level data. 242 */ 243 umtx_init(mutex); 244 m = (ICUMutex *)*mutex; 245 } 246 U_ASSERT(m->owner == mutex); 247 248 if (pMutexLockFn != NULL) { 249 (*pMutexLockFn)(gMutexContext, &m->userMutex); 250 } else { 251 PLATFORM_MUTEX_LOCK(&m->platformMutex); 252 } 253 254 #if defined(U_DEBUG) 255 m->recursionCount++; /* Recursion causes deadlock on Unixes. */ 256 U_ASSERT(m->recursionCount == 1); /* Recursion detection works on Windows. */ 257 /* Assertion failure on non-Windows indicates a */ 258 /* problem with the mutex implementation itself. */ 259 #endif 260 } 261 262 263 264 /* 265 * umtx_unlock 266 */ 267 U_CAPI void U_EXPORT2 268 umtx_unlock(UMTX* mutex) 269 { 270 ICUMutex *m; 271 if(mutex == NULL) { 272 mutex = &globalUMTX; 273 } 274 m = (ICUMutex *)*mutex; 275 if (m == NULL) { 276 U_ASSERT(FALSE); /* This mutex is not initialized. */ 277 return; 278 } 279 U_ASSERT(m->owner == mutex); 280 281 #if defined (U_DEBUG) 282 m->recursionCount--; 283 U_ASSERT(m->recursionCount == 0); /* Detect unlock of an already unlocked mutex */ 284 #endif 285 286 if (pMutexUnlockFn) { 287 (*pMutexUnlockFn)(gMutexContext, &m->userMutex); 288 } else { 289 PLATFORM_MUTEX_UNLOCK(&m->platformMutex); 290 } 291 } 292 293 294 /* umtx_ct Allocate and initialize a new ICUMutex. 295 * If a non-null pointer is supplied, initialize an existing ICU Mutex. 296 */ 297 static ICUMutex *umtx_ct(ICUMutex *m) { 298 if (m == NULL) { 299 m = (ICUMutex *)uprv_malloc(sizeof(ICUMutex)); 300 m->heapAllocated = TRUE; 301 } 302 m->next = NULL; /* List of mutexes is maintained at a higher level. */ 303 m->recursionCount = 0; 304 m->userMutex = NULL; 305 if (pMutexInitFn != NULL) { 306 UErrorCode status = U_ZERO_ERROR; 307 (*pMutexInitFn)(gMutexContext, &m->userMutex, &status); 308 U_ASSERT(U_SUCCESS(status)); 309 } else { 310 PLATFORM_MUTEX_INIT(&m->platformMutex); 311 } 312 return m; 313 } 314 315 316 /* umtx_dt Delete a ICUMutex. Destroy the underlying OS Platform mutex. 317 * Does not touch the linked list of ICU Mutexes. 318 */ 319 static void umtx_dt(ICUMutex *m) { 320 if (pMutexDestroyFn != NULL) { 321 (*pMutexDestroyFn)(gMutexContext, &m->userMutex); 322 m->userMutex = NULL; 323 } else { 324 PLATFORM_MUTEX_DESTROY(&m->platformMutex); 325 } 326 327 if (m->heapAllocated) { 328 uprv_free(m); 329 } 330 } 331 332 333 U_CAPI void U_EXPORT2 334 umtx_init(UMTX *mutex) { 335 ICUMutex *m = NULL; 336 void *originalValue; 337 338 if (*mutex != NULL) { 339 /* Mutex is already initialized. 340 * Multiple umtx_init()s of a UMTX by other ICU code are explicitly permitted. 341 */ 342 return; 343 } 344 #if defined(POSIX) 345 if (mutex == &globalUMTX) { 346 m = &globalMutex; 347 } 348 #endif 349 350 m = umtx_ct(m); 351 originalValue = SYNC_COMPARE_AND_SWAP(mutex, NULL, m); 352 if (originalValue != NULL) { 353 umtx_dt(m); 354 return; 355 } 356 357 m->owner = mutex; 358 359 /* Hook the new mutex into the list of all ICU mutexes, so that we can find and 360 * delete it for u_cleanup(). 361 */ 362 363 umtx_lock(NULL); 364 m->next = mutexListHead; 365 mutexListHead = m; 366 umtx_unlock(NULL); 367 return; 368 } 369 370 371 /* 372 * umtx_destroy. Un-initialize a mutex, releasing any underlying resources 373 * that it may be holding. Destroying an already destroyed 374 * mutex has no effect. Unlike umtx_init(), this function 375 * is not thread safe; two threads must not concurrently try to 376 * destroy the same mutex. 377 */ 378 U_CAPI void U_EXPORT2 379 umtx_destroy(UMTX *mutex) { 380 ICUMutex *m; 381 382 /* No one should be deleting the global ICU mutex. 383 * (u_cleanup() does delete it, but does so explicitly, not by passing NULL) 384 */ 385 U_ASSERT(mutex != NULL); 386 if (mutex == NULL) { 387 return; 388 } 389 390 m = (ICUMutex *)*mutex; 391 if (m == NULL) { /* Mutex not initialized, or already destroyed. */ 392 return; 393 } 394 395 U_ASSERT(m->owner == mutex); 396 if (m->owner != mutex) { 397 return; 398 } 399 400 /* Remove this mutex from the linked list of mutexes. */ 401 umtx_lock(NULL); 402 if (mutexListHead == m) { 403 mutexListHead = m->next; 404 } else { 405 ICUMutex *prev; 406 for (prev = mutexListHead; prev!=NULL && prev->next!=m; prev = prev->next); 407 /* Empty for loop body */ 408 if (prev != NULL) { 409 prev->next = m->next; 410 } 411 } 412 umtx_unlock(NULL); 413 414 umtx_dt(m); /* Delete the internal ICUMutex */ 415 *mutex = NULL; /* Clear the caller's UMTX */ 416 } 417 418 419 420 U_CAPI void U_EXPORT2 421 u_setMutexFunctions(const void *context, UMtxInitFn *i, UMtxFn *d, UMtxFn *l, UMtxFn *u, 422 UErrorCode *status) { 423 if (U_FAILURE(*status)) { 424 return; 425 } 426 427 /* Can not set a mutex function to a NULL value */ 428 if (i==NULL || d==NULL || l==NULL || u==NULL) { 429 *status = U_ILLEGAL_ARGUMENT_ERROR; 430 return; 431 } 432 433 /* If ICU is not in an initial state, disallow this operation. */ 434 if (cmemory_inUse()) { 435 *status = U_INVALID_STATE_ERROR; 436 return; 437 } 438 439 /* Kill any existing global mutex. POSIX platforms have a global mutex 440 * even before any other part of ICU is initialized. 441 */ 442 umtx_destroy(&globalUMTX); 443 444 /* Swap in the mutex function pointers. */ 445 pMutexInitFn = i; 446 pMutexDestroyFn = d; 447 pMutexLockFn = l; 448 pMutexUnlockFn = u; 449 gMutexContext = context; 450 451 #if defined (POSIX) 452 /* POSIX platforms must have a pre-initialized global mutex 453 * to allow other mutexes to initialize safely. */ 454 umtx_init(&globalUMTX); 455 #endif 456 } 457 458 459 /* synchronized compare and swap function, for use when OS or compiler built-in 460 * equivalents aren't available. 461 * 462 * This operation relies on the ICU global mutex for synchronization. 463 * 464 * There are two cases where this function can be entered when the global mutex is not 465 * yet initialized - at the end u_cleanup(), and at the end of u_setMutexFunctions, both 466 * of which re-init the global mutex. But neither function is thread-safe, so the lack of 467 * synchronization at these points doesn't matter. 468 */ 469 static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval) { 470 void *temp; 471 UBool needUnlock = FALSE; 472 473 if (globalUMTX != NULL) { 474 umtx_lock(&globalUMTX); 475 needUnlock = TRUE; 476 } 477 478 temp = *dest; 479 if (temp == oldval) { 480 *dest = newval; 481 } 482 483 if (needUnlock) { 484 umtx_unlock(&globalUMTX); 485 } 486 return temp; 487 } 488 489 490 491 /*----------------------------------------------------------------- 492 * 493 * Atomic Increment and Decrement 494 * umtx_atomic_inc 495 * umtx_atomic_dec 496 * 497 *----------------------------------------------------------------*/ 498 499 /* Pointers to user-supplied inc/dec functions. Null if no funcs have been set. */ 500 static UMtxAtomicFn *pIncFn = NULL; 501 static UMtxAtomicFn *pDecFn = NULL; 502 static const void *gIncDecContext = NULL; 503 504 static UMTX gIncDecMutex = NULL; 505 506 U_CAPI int32_t U_EXPORT2 507 umtx_atomic_inc(int32_t *p) { 508 int32_t retVal; 509 if (pIncFn) { 510 retVal = (*pIncFn)(gIncDecContext, p); 511 } else { 512 #if defined (U_WINDOWS) && ICU_USE_THREADS == 1 513 retVal = InterlockedIncrement((LONG*)p); 514 #elif defined(USE_MAC_OS_ATOMIC_INCREMENT) 515 retVal = OSAtomicIncrement32Barrier(p); 516 #elif (U_HAVE_GCC_ATOMICS == 1) 517 retVal = __sync_add_and_fetch(p, 1); 518 #elif defined (POSIX) && ICU_USE_THREADS == 1 519 umtx_lock(&gIncDecMutex); 520 retVal = ++(*p); 521 umtx_unlock(&gIncDecMutex); 522 #else 523 /* Unknown Platform, or ICU thread support compiled out. */ 524 retVal = ++(*p); 525 #endif 526 } 527 return retVal; 528 } 529 530 U_CAPI int32_t U_EXPORT2 531 umtx_atomic_dec(int32_t *p) { 532 int32_t retVal; 533 if (pDecFn) { 534 retVal = (*pDecFn)(gIncDecContext, p); 535 } else { 536 #if defined (U_WINDOWS) && ICU_USE_THREADS == 1 537 retVal = InterlockedDecrement((LONG*)p); 538 #elif defined(USE_MAC_OS_ATOMIC_INCREMENT) 539 retVal = OSAtomicDecrement32Barrier(p); 540 #elif (U_HAVE_GCC_ATOMICS == 1) 541 retVal = __sync_sub_and_fetch(p, 1); 542 #elif defined (POSIX) && ICU_USE_THREADS == 1 543 umtx_lock(&gIncDecMutex); 544 retVal = --(*p); 545 umtx_unlock(&gIncDecMutex); 546 #else 547 /* Unknown Platform, or ICU thread support compiled out. */ 548 retVal = --(*p); 549 #endif 550 } 551 return retVal; 552 } 553 554 555 556 U_CAPI void U_EXPORT2 557 u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *ip, UMtxAtomicFn *dp, 558 UErrorCode *status) { 559 if (U_FAILURE(*status)) { 560 return; 561 } 562 /* Can not set a mutex function to a NULL value */ 563 if (ip==NULL || dp==NULL) { 564 *status = U_ILLEGAL_ARGUMENT_ERROR; 565 return; 566 } 567 /* If ICU is not in an initial state, disallow this operation. */ 568 if (cmemory_inUse()) { 569 *status = U_INVALID_STATE_ERROR; 570 return; 571 } 572 573 pIncFn = ip; 574 pDecFn = dp; 575 gIncDecContext = context; 576 577 #if !U_RELEASE 578 { 579 int32_t testInt = 0; 580 U_ASSERT(umtx_atomic_inc(&testInt) == 1); /* Sanity Check. Do the functions work at all? */ 581 U_ASSERT(testInt == 1); 582 U_ASSERT(umtx_atomic_dec(&testInt) == 0); 583 U_ASSERT(testInt == 0); 584 } 585 #endif 586 } 587 588 589 590 /* 591 * Mutex Cleanup Function 592 * 593 * Destroy the global mutex(es), and reset the mutex function callback pointers. 594 */ 595 U_CFUNC UBool umtx_cleanup(void) { 596 ICUMutex *thisMutex = NULL; 597 ICUMutex *nextMutex = NULL; 598 599 /* Extra, do-nothing function call to suppress compiler warnings on platforms where 600 * mutexed_compare_and_swap is not otherwise used. */ 601 mutexed_compare_and_swap(&globalUMTX, NULL, NULL); 602 603 /* Delete all of the ICU mutexes. Do the global mutex last because it is used during 604 * the umtx_destroy operation of other mutexes. 605 */ 606 for (thisMutex=mutexListHead; thisMutex!=NULL; thisMutex=nextMutex) { 607 UMTX *umtx = thisMutex->owner; 608 nextMutex = thisMutex->next; 609 U_ASSERT(*umtx = (void *)thisMutex); 610 if (umtx != &globalUMTX) { 611 umtx_destroy(umtx); 612 } 613 } 614 umtx_destroy(&globalUMTX); 615 616 pMutexInitFn = NULL; 617 pMutexDestroyFn = NULL; 618 pMutexLockFn = NULL; 619 pMutexUnlockFn = NULL; 620 gMutexContext = NULL; 621 pIncFn = NULL; 622 pDecFn = NULL; 623 gIncDecContext = NULL; 624 gIncDecMutex = NULL; 625 626 #if defined (POSIX) 627 /* POSIX platforms must come out of u_cleanup() with a functioning global mutex 628 * to permit the safe resumption of use of ICU in multi-threaded environments. 629 */ 630 umtx_init(&globalUMTX); 631 #endif 632 return TRUE; 633 } 634 635 636