2
******************************************************************************
4
* Copyright (C) 1997-2008, International Business Machines
5
* Corporation and others. All Rights Reserved.
7
******************************************************************************
11
* Modification History:
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
******************************************************************************
21
#include "unicode/utypes.h"
26
#include <AvailabilityMacros.h>
27
#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)
28
#if defined(__STRICT_ANSI__)
29
#define UPRV_REMAP_INLINE
32
#include <libkern/OSAtomic.h>
33
#define USE_MAC_OS_ATOMIC_INCREMENT 1
34
#if defined(UPRV_REMAP_INLINE)
36
#undef UPRV_REMAP_INLINE
41
/* Assume POSIX, and modify as necessary below */
44
#if defined(U_WINDOWS)
47
#if defined(macintosh)
54
#if defined(POSIX) && (ICU_USE_THREADS==1)
55
# include <pthread.h> /* must be first, so that we get the multithread versions of things. */
57
#endif /* POSIX && (ICU_USE_THREADS==1) */
60
# define WIN32_LEAN_AND_MEAN
73
* A note on ICU Mutex Initialization and ICU startup:
75
* ICU mutexes, as used through the rest of the ICU code, are self-initializing.
76
* To make this work, ICU uses the _ICU GLobal Mutex_ to synchronize the lazy init
77
* of other ICU mutexes. For the global mutex itself, we need some other mechanism
78
* to safely initialize it on first use. This becomes important if two or more
79
* threads were more or less simultaenously the first to use ICU in a process, and
80
* were racing into the mutex initialization code.
82
* The solution for the global mutex init is platform dependent.
83
* On POSIX systems, C-style init can be used on a mutex, with the
84
* macro PTHREAD_MUTEX_INITIALIZER. The mutex is then ready for use, without
85
* first calling pthread_mutex_init().
87
* Windows has no equivalent statically initialized mutex or CRITICAL SECION.
88
* InitializeCriticalSection() must be called. If the global mutex does not
89
* appear to be initialized, a thread will create and initialize a new
90
* CRITICAL_SECTION, then use a Windows InterlockedCompareAndExchange to
91
* avoid problems with race conditions.
93
* If an application has overridden the ICU mutex implementation
94
* by calling u_setMutexFunctions(), the user supplied init function must
95
* be safe in the event that multiple threads concurrently attempt to init
96
* the same mutex. The first thread should do the init, and the others should
101
#define MAX_MUTEXES 40
102
static UMTX gGlobalMutex = NULL;
103
static UMTX gIncDecMutex = NULL;
104
#if (ICU_USE_THREADS == 1)
105
static UBool gMutexPoolInitialized = FALSE;
106
static char gMutexesInUse[MAX_MUTEXES];
108
#if defined(U_WINDOWS)
109
/*-------------------------------------------------------------
111
* WINDOWS platform variable declarations
113
*-------------------------------------------------------------*/
114
static CRITICAL_SECTION gMutexes[MAX_MUTEXES];
115
static CRITICAL_SECTION gGlobalWinMutex;
118
/* On WIN32 mutexes are reentrant. This makes it difficult to debug
119
* deadlocking problems that show up on POSIXy platforms, where
120
* mutexes deadlock upon reentry. ICU contains checking code for
121
* the global mutex as well as for other mutexes in the pool.
123
* This is for debugging purposes.
125
* This has no effect on non-WIN32 platforms, non-DEBUG builds, and
126
* non-ICU_USE_THREADS builds.
128
* Note: The CRITICAL_SECTION structure already has a RecursionCount
129
* member that can be used for this purpose, but portability to
130
* Win98/NT/2K needs to be tested before use. Works fine on XP.
131
* After portability is confirmed, the built-in RecursionCount can be
132
* used, and the gRecursionCountPool can be removed.
134
* Note: Non-global mutex checking only happens if there is no custom
135
* pMutexLockFn defined. Use one function, not two (don't use
136
* pMutexLockFn and pMutexUnlockFn) so the increment and decrement of
137
* the recursion count don't get out of sync. Users might set just
138
* one function, e.g., to perform a custom action, followed by a
139
* standard call to EnterCriticalSection.
141
#if defined(U_DEBUG) && (ICU_USE_THREADS==1)
142
static int32_t gRecursionCount = 0; /* detect global mutex locking */
143
static int32_t gRecursionCountPool[MAX_MUTEXES]; /* ditto for non-global */
148
/*-------------------------------------------------------------
150
* POSIX platform variable declarations
152
*-------------------------------------------------------------*/
153
static pthread_mutex_t gMutexes[MAX_MUTEXES] = {
154
PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
155
PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
156
PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
157
PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
158
PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
159
PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
160
PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER
164
/*-------------------------------------------------------------
166
* UNKNOWN platform declarations
168
*-------------------------------------------------------------*/
169
static void *gMutexes[MAX_MUTEXES] = {
178
/* Unknown platform. OK so long as ICU_USE_THREAD is not set.
179
Note that user can still set mutex functions at run time,
180
and that the global mutex variable is still needed in that case. */
181
#if (ICU_USE_THREADS == 1)
182
#error no ICU mutex implementation for this platform
185
#endif /* ICU_USE_THREADS==1 */
191
* User mutex implementation functions. If non-null, call back to these rather than
192
* directly using the system (Posix or Windows) APIs.
193
* (declarations are in uclean.h)
195
static UMtxInitFn *pMutexInitFn = NULL;
196
static UMtxFn *pMutexDestroyFn = NULL;
197
static UMtxFn *pMutexLockFn = NULL;
198
static UMtxFn *pMutexUnlockFn = NULL;
199
static const void *gMutexContext = NULL;
206
U_CAPI void U_EXPORT2
207
umtx_lock(UMTX *mutex)
210
mutex = &gGlobalMutex;
213
if (*mutex == NULL) {
214
/* Lock of an uninitialized mutex. Initialize it before proceeding. */
218
if (pMutexLockFn != NULL) {
219
(*pMutexLockFn)(gMutexContext, mutex);
222
#if (ICU_USE_THREADS == 1)
223
#if defined(U_WINDOWS)
224
EnterCriticalSection((CRITICAL_SECTION*) *mutex);
226
pthread_mutex_lock((pthread_mutex_t*) *mutex);
227
#endif /* cascade of platforms */
228
#endif /* ICU_USE_THREADS==1 */
231
#if defined(U_WINDOWS) && defined(U_DEBUG) && (ICU_USE_THREADS==1)
232
if (mutex == &gGlobalMutex) { /* Detect Reentrant locking of the global mutex. */
233
gRecursionCount++; /* Recursion causes deadlocks on Unixes. */
234
U_ASSERT(gRecursionCount == 1); /* Detection works on Windows. Debug problems there. */
236
/* This handles gGlobalMutex too, but only if there is no pMutexLockFn */
237
else if (pMutexLockFn == NULL) { /* see comments above */
238
size_t i = ((CRITICAL_SECTION*)*mutex) - &gMutexes[0];
239
U_ASSERT(i >= 0 && i < MAX_MUTEXES);
240
++gRecursionCountPool[i];
242
U_ASSERT(gRecursionCountPool[i] == 1); /* !Detect Deadlock! */
244
/* This works and is fast, but needs testing on Win98/NT/2K.
245
See comments above. [alan]
246
U_ASSERT((CRITICAL_SECTION*)*mutex >= &gMutexes[0] &&
247
(CRITICAL_SECTION*)*mutex <= &gMutexes[MAX_MUTEXES]);
248
U_ASSERT(((CRITICAL_SECTION*)*mutex)->RecursionCount == 1);
259
U_CAPI void U_EXPORT2
260
umtx_unlock(UMTX* mutex)
263
mutex = &gGlobalMutex;
267
#if (ICU_USE_THREADS == 1)
268
U_ASSERT(FALSE); /* This mutex is not initialized. */
273
#if defined (U_WINDOWS) && defined (U_DEBUG) && (ICU_USE_THREADS==1)
274
if (mutex == &gGlobalMutex) {
276
U_ASSERT(gRecursionCount == 0); /* Detect unlock of an already unlocked mutex */
278
/* This handles gGlobalMutex too, but only if there is no pMutexLockFn */
279
else if (pMutexLockFn == NULL) { /* see comments above */
280
size_t i = ((CRITICAL_SECTION*)*mutex) - &gMutexes[0];
281
U_ASSERT(i >= 0 && i < MAX_MUTEXES);
282
--gRecursionCountPool[i];
284
U_ASSERT(gRecursionCountPool[i] == 0); /* !Detect Deadlock! */
286
/* This works and is fast, but needs testing on Win98/NT/2K.
287
Note that RecursionCount will be 1, not 0, since we haven't
288
left the CRITICAL_SECTION yet. See comments above. [alan]
289
U_ASSERT((CRITICAL_SECTION*)*mutex >= &gMutexes[0] &&
290
(CRITICAL_SECTION*)*mutex <= &gMutexes[MAX_MUTEXES]);
291
U_ASSERT(((CRITICAL_SECTION*)*mutex)->RecursionCount == 1);
296
if (pMutexUnlockFn) {
297
(*pMutexUnlockFn)(gMutexContext, mutex);
299
#if (ICU_USE_THREADS==1)
300
#if defined (U_WINDOWS)
301
LeaveCriticalSection((CRITICAL_SECTION*)*mutex);
302
#elif defined (POSIX)
303
pthread_mutex_unlock((pthread_mutex_t*)*mutex);
304
#endif /* cascade of platforms */
305
#endif /* ICU_USE_THREADS == 1 */
313
* initGlobalMutex Do the platform specific initialization of the ICU global mutex.
314
* Separated out from the other mutexes because it is different:
315
* Mutex storage is static for POSIX, init must be thread safe
316
* without the use of another mutex.
318
static void initGlobalMutex() {
320
* If User Supplied mutex functions are in use
321
* init the icu global mutex using them.
323
if (pMutexInitFn != NULL) {
324
if (gGlobalMutex==NULL) {
325
UErrorCode status = U_ZERO_ERROR;
326
(*pMutexInitFn)(gMutexContext, &gGlobalMutex, &status);
327
if (U_FAILURE(status)) {
328
/* TODO: how should errors here be handled? */
335
/* No user override of mutex functions.
336
* Use default ICU mutex implementations.
338
#if (ICU_USE_THREADS == 1)
340
* for Windows, init the pool of critical sections that we
341
* will use as needed for ICU mutexes.
343
#if defined (U_WINDOWS)
344
if (gMutexPoolInitialized == FALSE) {
346
for (i=0; i<MAX_MUTEXES; i++) {
347
InitializeCriticalSection(&gMutexes[i]);
348
#if defined (U_DEBUG)
349
gRecursionCountPool[i] = 0; /* see comments above */
352
gMutexPoolInitialized = TRUE;
354
#elif defined (U_DARWIN)
355
/* PTHREAD_MUTEX_INITIALIZER works, don't need to call pthread_mutex_init
356
* as below (which is subject to a race condition)
358
gMutexPoolInitialized = TRUE;
359
#elif defined (POSIX)
360
/* TODO: experimental code. Shouldn't need to explicitly init the mutexes. */
361
if (gMutexPoolInitialized == FALSE) {
363
for (i=0; i<MAX_MUTEXES; i++) {
364
pthread_mutex_init(&gMutexes[i], NULL);
366
gMutexPoolInitialized = TRUE;
371
* for both Windows & POSIX, the first mutex in the array is used
372
* for the ICU global mutex.
374
gGlobalMutex = &gMutexes[0];
375
gMutexesInUse[0] = 1;
377
#else /* ICU_USE_THREADS */
378
gGlobalMutex = &gGlobalMutex; /* With no threads, we must still set the mutex to
379
* some non-null value to make the rest of the
380
* (not ifdefed) mutex code think that it is initialized.
382
#endif /* ICU_USE_THREADS */
389
U_CAPI void U_EXPORT2
390
umtx_init(UMTX *mutex)
392
if (mutex == NULL || mutex == &gGlobalMutex) {
396
if (*mutex != NULL) {
397
/* Another thread initialized this mutex first. */
402
if (pMutexInitFn != NULL) {
403
UErrorCode status = U_ZERO_ERROR;
404
(*pMutexInitFn)(gMutexContext, mutex, &status);
405
/* TODO: how to report failure on init? */
410
#if (ICU_USE_THREADS == 1)
411
/* Search through our pool of pre-allocated mutexes for one that is not
414
for (i=0; i<MAX_MUTEXES; i++) {
415
if (gMutexesInUse[i] == 0) {
416
gMutexesInUse[i] = 1;
417
*mutex = &gMutexes[i];
425
#if (ICU_USE_THREADS == 1)
426
/* No more mutexes were available from our pre-allocated pool. */
427
/* TODO: how best to deal with this? */
428
U_ASSERT(*mutex != NULL);
435
* umtx_destroy. Un-initialize a mutex, releasing any underlying resources
436
* that it may be holding. Destroying an already destroyed
437
* mutex has no effect. Unlike umtx_init(), this function
438
* is not thread safe; two threads must not concurrently try to
439
* destroy the same mutex.
441
U_CAPI void U_EXPORT2
442
umtx_destroy(UMTX *mutex) {
443
if (mutex == NULL) { /* destroy the global mutex */
444
mutex = &gGlobalMutex;
447
if (*mutex == NULL) { /* someone already did it. */
451
/* The life of the inc/dec mutex is tied to that of the global mutex. */
452
if (mutex == &gGlobalMutex) {
453
umtx_destroy(&gIncDecMutex);
456
if (pMutexDestroyFn != NULL) {
457
/* Mutexes are being managed by the app. Call back to it for the destroy. */
458
(*pMutexDestroyFn)(gMutexContext, mutex);
461
#if (ICU_USE_THREADS == 1)
462
/* Return this mutex to the pool of available mutexes, if it came from the
463
* pool in the first place.
465
/* TODO use pointer math here, instead of iterating! */
467
for (i=0; i<MAX_MUTEXES; i++) {
468
if (*mutex == &gMutexes[i]) {
469
gMutexesInUse[i] = 0;
481
U_CAPI void U_EXPORT2
482
u_setMutexFunctions(const void *context, UMtxInitFn *i, UMtxFn *d, UMtxFn *l, UMtxFn *u,
483
UErrorCode *status) {
484
if (U_FAILURE(*status)) {
488
/* Can not set a mutex function to a NULL value */
489
if (i==NULL || d==NULL || l==NULL || u==NULL) {
490
*status = U_ILLEGAL_ARGUMENT_ERROR;
494
/* If ICU is not in an initial state, disallow this operation. */
495
if (cmemory_inUse()) {
496
*status = U_INVALID_STATE_ERROR;
500
/* Swap in the mutex function pointers. */
505
gMutexContext = context;
506
gGlobalMutex = NULL; /* For POSIX, the global mutex will be pre-initialized */
507
/* Undo that, force re-initialization when u_init() */
513
/*-----------------------------------------------------------------
515
* Atomic Increment and Decrement
519
*----------------------------------------------------------------*/
521
/* Pointers to user-supplied inc/dec functions. Null if no funcs have been set. */
522
static UMtxAtomicFn *pIncFn = NULL;
523
static UMtxAtomicFn *pDecFn = NULL;
524
static const void *gIncDecContext = NULL;
527
U_CAPI int32_t U_EXPORT2
528
umtx_atomic_inc(int32_t *p) {
531
retVal = (*pIncFn)(gIncDecContext, p);
533
#if defined (U_WINDOWS) && ICU_USE_THREADS == 1
534
retVal = InterlockedIncrement((LONG*)p);
535
#elif defined(USE_MAC_OS_ATOMIC_INCREMENT)
536
retVal = OSAtomicIncrement32Barrier(p);
537
#elif defined (POSIX) && ICU_USE_THREADS == 1
538
umtx_lock(&gIncDecMutex);
540
umtx_unlock(&gIncDecMutex);
542
/* Unknown Platform, or ICU thread support compiled out. */
549
U_CAPI int32_t U_EXPORT2
550
umtx_atomic_dec(int32_t *p) {
553
retVal = (*pDecFn)(gIncDecContext, p);
555
#if defined (U_WINDOWS) && ICU_USE_THREADS == 1
556
retVal = InterlockedDecrement((LONG*)p);
557
#elif defined(USE_MAC_OS_ATOMIC_INCREMENT)
558
retVal = OSAtomicDecrement32Barrier(p);
559
#elif defined (POSIX) && ICU_USE_THREADS == 1
560
umtx_lock(&gIncDecMutex);
562
umtx_unlock(&gIncDecMutex);
564
/* Unknown Platform, or ICU thread support compiled out. */
571
/* TODO: Some POSIXy platforms have atomic inc/dec functions available. Use them. */
577
U_CAPI void U_EXPORT2
578
u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *ip, UMtxAtomicFn *dp,
579
UErrorCode *status) {
580
if (U_FAILURE(*status)) {
583
/* Can not set a mutex function to a NULL value */
584
if (ip==NULL || dp==NULL) {
585
*status = U_ILLEGAL_ARGUMENT_ERROR;
588
/* If ICU is not in an initial state, disallow this operation. */
589
if (cmemory_inUse()) {
590
*status = U_INVALID_STATE_ERROR;
596
gIncDecContext = context;
601
U_ASSERT(umtx_atomic_inc(&testInt) == 1); /* Sanity Check. Do the functions work at all? */
602
U_ASSERT(testInt == 1);
603
U_ASSERT(umtx_atomic_dec(&testInt) == 0);
604
U_ASSERT(testInt == 0);
612
* Mutex Cleanup Function
614
* Destroy the global mutex(es), and reset the mutex function callback pointers.
616
U_CFUNC UBool umtx_cleanup(void) {
619
pMutexDestroyFn = NULL;
621
pMutexUnlockFn = NULL;
622
gMutexContext = NULL;
626
gIncDecContext = NULL;
629
#if (ICU_USE_THREADS == 1)
630
if (gMutexPoolInitialized) {
632
for (i=0; i<MAX_MUTEXES; i++) {
633
if (gMutexesInUse[i]) {
634
#if defined (U_WINDOWS)
635
DeleteCriticalSection(&gMutexes[i]);
636
#elif defined (POSIX)
637
pthread_mutex_destroy(&gMutexes[i]);
639
gMutexesInUse[i] = 0;
643
gMutexPoolInitialized = FALSE;