2 ******************************************************************************
4 * Copyright (C) 1997-2006, 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 #include <libkern/OSAtomic.h>
29 #define USE_MAC_OS_ATOMIC_INCREMENT 1
33 /* Assume POSIX, and modify as necessary below */
36 #if defined(U_WINDOWS)
39 #if defined(macintosh)
46 #if defined(POSIX) && (ICU_USE_THREADS==1)
47 # include <pthread.h> /* must be first, so that we get the multithread versions of things. */
49 #endif /* POSIX && (ICU_USE_THREADS==1) */
52 # define WIN32_LEAN_AND_MEAN
65 * A note on ICU Mutex Initialization and ICU startup:
67 * ICU mutexes, as used through the rest of the ICU code, are self-initializing.
68 * To make this work, ICU uses the _ICU GLobal Mutex_ to synchronize the lazy init
69 * of other ICU mutexes. For the global mutex itself, we need some other mechanism
70 * to safely initialize it on first use. This becomes important if two or more
71 * threads were more or less simultaenously the first to use ICU in a process, and
72 * were racing into the mutex initialization code.
74 * The solution for the global mutex init is platform dependent.
75 * On POSIX systems, C-style init can be used on a mutex, with the
76 * macro PTHREAD_MUTEX_INITIALIZER. The mutex is then ready for use, without
77 * first calling pthread_mutex_init().
79 * Windows has no equivalent statically initialized mutex or CRITICAL SECION.
80 * InitializeCriticalSection() must be called. If the global mutex does not
81 * appear to be initialized, a thread will create and initialize a new
82 * CRITICAL_SECTION, then use a Windows InterlockedCompareAndExchange to
83 * avoid problems with race conditions.
85 * If an application has overridden the ICU mutex implementation
86 * by calling u_setMutexFunctions(), the user supplied init function must
87 * be safe in the event that multiple threads concurrently attempt to init
88 * the same mutex. The first thread should do the init, and the others should
93 #define MAX_MUTEXES 30
94 static UMTX gGlobalMutex
= NULL
;
95 static UMTX gIncDecMutex
= NULL
;
96 #if (ICU_USE_THREADS == 1)
97 static UBool gMutexPoolInitialized
= FALSE
;
98 static char gMutexesInUse
[MAX_MUTEXES
];
100 #if defined(U_WINDOWS)
101 /*-------------------------------------------------------------
103 * WINDOWS platform variable declarations
105 *-------------------------------------------------------------*/
106 static CRITICAL_SECTION gMutexes
[MAX_MUTEXES
];
107 static CRITICAL_SECTION gGlobalWinMutex
;
110 /* On WIN32 mutexes are reentrant. This makes it difficult to debug
111 * deadlocking problems that show up on POSIXy platforms, where
112 * mutexes deadlock upon reentry. ICU contains checking code for
113 * the global mutex as well as for other mutexes in the pool.
115 * This is for debugging purposes.
117 * This has no effect on non-WIN32 platforms, non-DEBUG builds, and
118 * non-ICU_USE_THREADS builds.
120 * Note: The CRITICAL_SECTION structure already has a RecursionCount
121 * member that can be used for this purpose, but portability to
122 * Win98/NT/2K needs to be tested before use. Works fine on XP.
123 * After portability is confirmed, the built-in RecursionCount can be
124 * used, and the gRecursionCountPool can be removed.
126 * Note: Non-global mutex checking only happens if there is no custom
127 * pMutexLockFn defined. Use one function, not two (don't use
128 * pMutexLockFn and pMutexUnlockFn) so the increment and decrement of
129 * the recursion count don't get out of sync. Users might set just
130 * one function, e.g., to perform a custom action, followed by a
131 * standard call to EnterCriticalSection.
133 #if defined(U_DEBUG) && (ICU_USE_THREADS==1)
134 static int32_t gRecursionCount
= 0; /* detect global mutex locking */
135 static int32_t gRecursionCountPool
[MAX_MUTEXES
]; /* ditto for non-global */
140 /*-------------------------------------------------------------
142 * POSIX platform variable declarations
144 *-------------------------------------------------------------*/
145 static pthread_mutex_t gMutexes
[MAX_MUTEXES
] = {
146 PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
,
147 PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
,
148 PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
,
149 PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
,
150 PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
,
151 PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
,
152 PTHREAD_MUTEX_INITIALIZER
, PTHREAD_MUTEX_INITIALIZER
156 /*-------------------------------------------------------------
158 * UNKNOWN platform declarations
160 *-------------------------------------------------------------*/
161 static void *gMutexes
[MAX_MUTEXES
] = {
170 /* Unknown platform. OK so long as ICU_USE_THREAD is not set.
171 Note that user can still set mutex functions at run time,
172 and that the global mutex variable is still needed in that case. */
173 #if (ICU_USE_THREADS == 1)
174 #error no ICU mutex implementation for this platform
177 #endif /* ICU_USE_THREADS==1 */
183 * User mutex implementation functions. If non-null, call back to these rather than
184 * directly using the system (Posix or Windows) APIs.
185 * (declarations are in uclean.h)
187 static UMtxInitFn
*pMutexInitFn
= NULL
;
188 static UMtxFn
*pMutexDestroyFn
= NULL
;
189 static UMtxFn
*pMutexLockFn
= NULL
;
190 static UMtxFn
*pMutexUnlockFn
= NULL
;
191 static const void *gMutexContext
= NULL
;
198 U_CAPI
void U_EXPORT2
199 umtx_lock(UMTX
*mutex
)
202 mutex
= &gGlobalMutex
;
205 if (*mutex
== NULL
) {
206 /* Lock of an uninitialized mutex. Initialize it before proceeding. */
210 if (pMutexLockFn
!= NULL
) {
211 (*pMutexLockFn
)(gMutexContext
, mutex
);
214 #if (ICU_USE_THREADS == 1)
215 #if defined(U_WINDOWS)
216 EnterCriticalSection((CRITICAL_SECTION
*) *mutex
);
218 pthread_mutex_lock((pthread_mutex_t
*) *mutex
);
219 #endif /* cascade of platforms */
220 #endif /* ICU_USE_THREADS==1 */
223 #if defined(U_WINDOWS) && defined(U_DEBUG) && (ICU_USE_THREADS==1)
224 if (mutex
== &gGlobalMutex
) { /* Detect Reentrant locking of the global mutex. */
225 gRecursionCount
++; /* Recursion causes deadlocks on Unixes. */
226 U_ASSERT(gRecursionCount
== 1); /* Detection works on Windows. Debug problems there. */
228 /* This handles gGlobalMutex too, but only if there is no pMutexLockFn */
229 else if (pMutexLockFn
== NULL
) { /* see comments above */
230 size_t i
= ((CRITICAL_SECTION
*)*mutex
) - &gMutexes
[0];
231 U_ASSERT(i
>= 0 && i
< MAX_MUTEXES
);
232 ++gRecursionCountPool
[i
];
234 U_ASSERT(gRecursionCountPool
[i
] == 1); /* !Detect Deadlock! */
236 /* This works and is fast, but needs testing on Win98/NT/2K.
237 See comments above. [alan]
238 U_ASSERT((CRITICAL_SECTION*)*mutex >= &gMutexes[0] &&
239 (CRITICAL_SECTION*)*mutex <= &gMutexes[MAX_MUTEXES]);
240 U_ASSERT(((CRITICAL_SECTION*)*mutex)->RecursionCount == 1);
251 U_CAPI
void U_EXPORT2
252 umtx_unlock(UMTX
* mutex
)
255 mutex
= &gGlobalMutex
;
259 #if (ICU_USE_THREADS == 1)
260 U_ASSERT(FALSE
); /* This mutex is not initialized. */
265 #if defined (U_WINDOWS) && defined (U_DEBUG) && (ICU_USE_THREADS==1)
266 if (mutex
== &gGlobalMutex
) {
268 U_ASSERT(gRecursionCount
== 0); /* Detect unlock of an already unlocked mutex */
270 /* This handles gGlobalMutex too, but only if there is no pMutexLockFn */
271 else if (pMutexLockFn
== NULL
) { /* see comments above */
272 size_t i
= ((CRITICAL_SECTION
*)*mutex
) - &gMutexes
[0];
273 U_ASSERT(i
>= 0 && i
< MAX_MUTEXES
);
274 --gRecursionCountPool
[i
];
276 U_ASSERT(gRecursionCountPool
[i
] == 0); /* !Detect Deadlock! */
278 /* This works and is fast, but needs testing on Win98/NT/2K.
279 Note that RecursionCount will be 1, not 0, since we haven't
280 left the CRITICAL_SECTION yet. See comments above. [alan]
281 U_ASSERT((CRITICAL_SECTION*)*mutex >= &gMutexes[0] &&
282 (CRITICAL_SECTION*)*mutex <= &gMutexes[MAX_MUTEXES]);
283 U_ASSERT(((CRITICAL_SECTION*)*mutex)->RecursionCount == 1);
288 if (pMutexUnlockFn
) {
289 (*pMutexUnlockFn
)(gMutexContext
, mutex
);
291 #if (ICU_USE_THREADS==1)
292 #if defined (U_WINDOWS)
293 LeaveCriticalSection((CRITICAL_SECTION
*)*mutex
);
294 #elif defined (POSIX)
295 pthread_mutex_unlock((pthread_mutex_t
*)*mutex
);
296 #endif /* cascade of platforms */
297 #endif /* ICU_USE_THREADS == 1 */
305 * initGlobalMutex Do the platform specific initialization of the ICU global mutex.
306 * Separated out from the other mutexes because it is different:
307 * Mutex storage is static for POSIX, init must be thread safe
308 * without the use of another mutex.
310 static void initGlobalMutex() {
312 * If User Supplied mutex functions are in use
313 * init the icu global mutex using them.
315 if (pMutexInitFn
!= NULL
) {
316 if (gGlobalMutex
==NULL
) {
317 UErrorCode status
= U_ZERO_ERROR
;
318 (*pMutexInitFn
)(gMutexContext
, &gGlobalMutex
, &status
);
319 if (U_FAILURE(status
)) {
320 /* TODO: how should errors here be handled? */
327 /* No user override of mutex functions.
328 * Use default ICU mutex implementations.
330 #if (ICU_USE_THREADS == 1)
332 * for Windows, init the pool of critical sections that we
333 * will use as needed for ICU mutexes.
335 #if defined (U_WINDOWS)
336 if (gMutexPoolInitialized
== FALSE
) {
338 for (i
=0; i
<MAX_MUTEXES
; i
++) {
339 InitializeCriticalSection(&gMutexes
[i
]);
340 #if defined (U_DEBUG)
341 gRecursionCountPool
[i
] = 0; /* see comments above */
344 gMutexPoolInitialized
= TRUE
;
346 #elif defined (POSIX)
347 /* TODO: experimental code. Shouldn't need to explicitly init the mutexes. */
348 if (gMutexPoolInitialized
== FALSE
) {
350 for (i
=0; i
<MAX_MUTEXES
; i
++) {
351 pthread_mutex_init(&gMutexes
[i
], NULL
);
353 gMutexPoolInitialized
= TRUE
;
358 * for both Windows & POSIX, the first mutex in the array is used
359 * for the ICU global mutex.
361 gGlobalMutex
= &gMutexes
[0];
362 gMutexesInUse
[0] = 1;
364 #else /* ICU_USE_THREADS */
365 gGlobalMutex
= &gGlobalMutex
; /* With no threads, we must still set the mutex to
366 * some non-null value to make the rest of the
367 * (not ifdefed) mutex code think that it is initialized.
369 #endif /* ICU_USE_THREADS */
376 U_CAPI
void U_EXPORT2
377 umtx_init(UMTX
*mutex
)
379 if (mutex
== NULL
|| mutex
== &gGlobalMutex
) {
383 if (*mutex
!= NULL
) {
384 /* Another thread initialized this mutex first. */
389 if (pMutexInitFn
!= NULL
) {
390 UErrorCode status
= U_ZERO_ERROR
;
391 (*pMutexInitFn
)(gMutexContext
, mutex
, &status
);
392 /* TODO: how to report failure on init? */
397 #if (ICU_USE_THREADS == 1)
398 /* Search through our pool of pre-allocated mutexes for one that is not
401 for (i
=0; i
<MAX_MUTEXES
; i
++) {
402 if (gMutexesInUse
[i
] == 0) {
403 gMutexesInUse
[i
] = 1;
404 *mutex
= &gMutexes
[i
];
412 #if (ICU_USE_THREADS == 1)
413 /* No more mutexes were available from our pre-allocated pool. */
414 /* TODO: how best to deal with this? */
415 U_ASSERT(*mutex
!= NULL
);
422 * umtx_destroy. Un-initialize a mutex, releasing any underlying resources
423 * that it may be holding. Destroying an already destroyed
424 * mutex has no effect. Unlike umtx_init(), this function
425 * is not thread safe; two threads must not concurrently try to
426 * destroy the same mutex.
428 U_CAPI
void U_EXPORT2
429 umtx_destroy(UMTX
*mutex
) {
430 if (mutex
== NULL
) { /* destroy the global mutex */
431 mutex
= &gGlobalMutex
;
434 if (*mutex
== NULL
) { /* someone already did it. */
438 /* The life of the inc/dec mutex is tied to that of the global mutex. */
439 if (mutex
== &gGlobalMutex
) {
440 umtx_destroy(&gIncDecMutex
);
443 if (pMutexDestroyFn
!= NULL
) {
444 /* Mutexes are being managed by the app. Call back to it for the destroy. */
445 (*pMutexDestroyFn
)(gMutexContext
, mutex
);
448 #if (ICU_USE_THREADS == 1)
449 /* Return this mutex to the pool of available mutexes, if it came from the
450 * pool in the first place.
452 /* TODO use pointer math here, instead of iterating! */
454 for (i
=0; i
<MAX_MUTEXES
; i
++) {
455 if (*mutex
== &gMutexes
[i
]) {
456 gMutexesInUse
[i
] = 0;
468 U_CAPI
void U_EXPORT2
469 u_setMutexFunctions(const void *context
, UMtxInitFn
*i
, UMtxFn
*d
, UMtxFn
*l
, UMtxFn
*u
,
470 UErrorCode
*status
) {
471 if (U_FAILURE(*status
)) {
475 /* Can not set a mutex function to a NULL value */
476 if (i
==NULL
|| d
==NULL
|| l
==NULL
|| u
==NULL
) {
477 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
481 /* If ICU is not in an initial state, disallow this operation. */
482 if (cmemory_inUse()) {
483 *status
= U_INVALID_STATE_ERROR
;
487 /* Swap in the mutex function pointers. */
492 gMutexContext
= context
;
493 gGlobalMutex
= NULL
; /* For POSIX, the global mutex will be pre-initialized */
494 /* Undo that, force re-initialization when u_init() */
500 /*-----------------------------------------------------------------
502 * Atomic Increment and Decrement
506 *----------------------------------------------------------------*/
508 /* Pointers to user-supplied inc/dec functions. Null if no funcs have been set. */
509 static UMtxAtomicFn
*pIncFn
= NULL
;
510 static UMtxAtomicFn
*pDecFn
= NULL
;
511 static const void *gIncDecContext
= NULL
;
514 U_CAPI
int32_t U_EXPORT2
515 umtx_atomic_inc(int32_t *p
) {
518 retVal
= (*pIncFn
)(gIncDecContext
, p
);
520 #if defined (U_WINDOWS) && ICU_USE_THREADS == 1
521 retVal
= InterlockedIncrement((LONG
*)p
);
522 #elif defined(USE_MAC_OS_ATOMIC_INCREMENT)
523 retVal
= OSAtomicIncrement32Barrier(p
);
524 #elif defined (POSIX) && ICU_USE_THREADS == 1
525 umtx_lock(&gIncDecMutex
);
527 umtx_unlock(&gIncDecMutex
);
529 /* Unknown Platform, or ICU thread support compiled out. */
536 U_CAPI
int32_t U_EXPORT2
537 umtx_atomic_dec(int32_t *p
) {
540 retVal
= (*pDecFn
)(gIncDecContext
, p
);
542 #if defined (U_WINDOWS) && ICU_USE_THREADS == 1
543 retVal
= InterlockedDecrement((LONG
*)p
);
544 #elif defined(USE_MAC_OS_ATOMIC_INCREMENT)
545 retVal
= OSAtomicDecrement32Barrier(p
);
546 #elif defined (POSIX) && ICU_USE_THREADS == 1
547 umtx_lock(&gIncDecMutex
);
549 umtx_unlock(&gIncDecMutex
);
551 /* Unknown Platform, or ICU thread support compiled out. */
558 /* TODO: Some POSIXy platforms have atomic inc/dec functions available. Use them. */
564 U_CAPI
void U_EXPORT2
565 u_setAtomicIncDecFunctions(const void *context
, UMtxAtomicFn
*ip
, UMtxAtomicFn
*dp
,
566 UErrorCode
*status
) {
567 if (U_FAILURE(*status
)) {
570 /* Can not set a mutex function to a NULL value */
571 if (ip
==NULL
|| dp
==NULL
) {
572 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
575 /* If ICU is not in an initial state, disallow this operation. */
576 if (cmemory_inUse()) {
577 *status
= U_INVALID_STATE_ERROR
;
583 gIncDecContext
= context
;
588 U_ASSERT(umtx_atomic_inc(&testInt
) == 1); /* Sanity Check. Do the functions work at all? */
589 U_ASSERT(testInt
== 1);
590 U_ASSERT(umtx_atomic_dec(&testInt
) == 0);
591 U_ASSERT(testInt
== 0);
599 * Mutex Cleanup Function
601 * Destroy the global mutex(es), and reset the mutex function callback pointers.
603 U_CFUNC UBool
umtx_cleanup(void) {
606 pMutexDestroyFn
= NULL
;
608 pMutexUnlockFn
= NULL
;
609 gMutexContext
= NULL
;
613 gIncDecContext
= NULL
;
616 #if (ICU_USE_THREADS == 1)
617 if (gMutexPoolInitialized
) {
619 for (i
=0; i
<MAX_MUTEXES
; i
++) {
620 if (gMutexesInUse
[i
]) {
621 #if defined (U_WINDOWS)
622 DeleteCriticalSection(&gMutexes
[i
]);
623 #elif defined (POSIX)
624 pthread_mutex_destroy(&gMutexes
[i
]);
626 gMutexesInUse
[i
] = 0;
630 gMutexPoolInitialized
= FALSE
;