OLD | NEW |
(Empty) | |
| 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-initializin
g. |
| 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 mech
anism |
| 83 * to safely initialize it on first use. This becomes important when two or m
ore |
| 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 deadl
ock |
| 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 asser
tion |
| 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 cod
e. |
| 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 t
his */ |
| 170 /* ICUMutex object.
*/ |
| 171 |
| 172 UBool heapAllocated; /* Set if this ICUMutex is heap allocated, a
nd */ |
| 173 /* will need to be deleted. The global mu
tex */ |
| 174 /* is static on POSIX platforms; all other
s */ |
| 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_clea
nup(). */ |
| 179 |
| 180 int32_t recursionCount; /* For debugging, detect recursive mutex loc
ks. */ |
| 181 |
| 182 MUTEX_TYPE platformMutex; /* The underlying OS mutex being wrapped.
*/ |
| 183 |
| 184 UMTX userMutex; /* For use with u_setMutexFunctions operatio
ns, */ |
| 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_INITI
ALIZER, 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 mut
exes. |
| 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 indica
tes 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 m
utex */ |
| 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 perm
itted. |
| 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 NUL
L) |
| 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->nex
t); |
| 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, UM
txFn *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-i
n |
| 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 mute
x is not |
| 465 * yet initialized - at the end u_cleanup(), and at the end of u_setMutexFunc
tions, 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 poin
ters. |
| 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 platform
s 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 us
ed 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 mu
tex |
| 628 * to permit the safe resumption of use of ICU in multi-threaded environment
s. |
| 629 */ |
| 630 umtx_init(&globalUMTX); |
| 631 #endif |
| 632 return TRUE; |
| 633 } |
| 634 |
| 635 |
OLD | NEW |