]> git.saurik.com Git - apple/icu.git/blame_incremental - icuSources/common/umutex.c
ICU-8.11.4.tar.gz
[apple/icu.git] / icuSources / common / umutex.c
... / ...
CommitLineData
1/*
2******************************************************************************
3*
4* Copyright (C) 1997-2006, International Business Machines
5* Corporation and others. All Rights Reserved.
6*
7******************************************************************************
8*
9* File CMUTEX.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#if defined(U_DARWIN)
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
30#endif
31#endif
32
33/* Assume POSIX, and modify as necessary below */
34#define POSIX
35
36#if defined(U_WINDOWS)
37#undef POSIX
38#endif
39#if defined(macintosh)
40#undef POSIX
41#endif
42#if defined(OS2)
43#undef POSIX
44#endif
45
46#if defined(POSIX) && (ICU_USE_THREADS==1)
47# include <pthread.h> /* must be first, so that we get the multithread versions of things. */
48
49#endif /* POSIX && (ICU_USE_THREADS==1) */
50
51#ifdef U_WINDOWS
52# define WIN32_LEAN_AND_MEAN
53# define VC_EXTRALEAN
54# define NOUSER
55# define NOSERVICE
56# define NOIME
57# define NOMCX
58# include <windows.h>
59#endif
60
61#include "umutex.h"
62#include "cmemory.h"
63
64/*
65 * A note on ICU Mutex Initialization and ICU startup:
66 *
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.
73 *
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().
78 *
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.
84 *
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
89 * have no effect.
90 *
91 */
92
93#define MAX_MUTEXES 30
94static UMTX gGlobalMutex = NULL;
95static UMTX gIncDecMutex = NULL;
96#if (ICU_USE_THREADS == 1)
97static UBool gMutexPoolInitialized = FALSE;
98static char gMutexesInUse[MAX_MUTEXES];
99
100#if defined(U_WINDOWS)
101/*-------------------------------------------------------------
102 *
103 * WINDOWS platform variable declarations
104 *
105 *-------------------------------------------------------------*/
106static CRITICAL_SECTION gMutexes[MAX_MUTEXES];
107static CRITICAL_SECTION gGlobalWinMutex;
108
109
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.
114 *
115 * This is for debugging purposes.
116 *
117 * This has no effect on non-WIN32 platforms, non-DEBUG builds, and
118 * non-ICU_USE_THREADS builds.
119 *
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.
125 *
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.
132 */
133#if defined(U_DEBUG) && (ICU_USE_THREADS==1)
134static int32_t gRecursionCount = 0; /* detect global mutex locking */
135static int32_t gRecursionCountPool[MAX_MUTEXES]; /* ditto for non-global */
136#endif
137
138
139#elif defined(POSIX)
140/*-------------------------------------------------------------
141 *
142 * POSIX platform variable declarations
143 *
144 *-------------------------------------------------------------*/
145static 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
153};
154
155#else
156/*-------------------------------------------------------------
157 *
158 * UNKNOWN platform declarations
159 *
160 *-------------------------------------------------------------*/
161static void *gMutexes[MAX_MUTEXES] = {
162 NULL, NULL, NULL,
163 NULL, NULL, NULL,
164 NULL, NULL, NULL,
165 NULL, NULL, NULL,
166 NULL, NULL, NULL,
167 NULL, NULL, NULL,
168 NULL, NULL };
169
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
175#endif
176#endif
177#endif /* ICU_USE_THREADS==1 */
178
179
180
181
182/*
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)
186 */
187static UMtxInitFn *pMutexInitFn = NULL;
188static UMtxFn *pMutexDestroyFn = NULL;
189static UMtxFn *pMutexLockFn = NULL;
190static UMtxFn *pMutexUnlockFn = NULL;
191static const void *gMutexContext = NULL;
192
193
194
195/*
196 * umtx_lock
197 */
198U_CAPI void U_EXPORT2
199umtx_lock(UMTX *mutex)
200{
201 if (mutex == NULL) {
202 mutex = &gGlobalMutex;
203 }
204
205 if (*mutex == NULL) {
206 /* Lock of an uninitialized mutex. Initialize it before proceeding. */
207 umtx_init(mutex);
208 }
209
210 if (pMutexLockFn != NULL) {
211 (*pMutexLockFn)(gMutexContext, mutex);
212 } else {
213
214#if (ICU_USE_THREADS == 1)
215#if defined(U_WINDOWS)
216 EnterCriticalSection((CRITICAL_SECTION*) *mutex);
217#elif defined(POSIX)
218 pthread_mutex_lock((pthread_mutex_t*) *mutex);
219#endif /* cascade of platforms */
220#endif /* ICU_USE_THREADS==1 */
221 }
222
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. */
227 }
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];
233
234 U_ASSERT(gRecursionCountPool[i] == 1); /* !Detect Deadlock! */
235
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);
241 */
242 }
243#endif /*U_DEBUG*/
244}
245
246
247
248/*
249 * umtx_unlock
250 */
251U_CAPI void U_EXPORT2
252umtx_unlock(UMTX* mutex)
253{
254 if(mutex == NULL) {
255 mutex = &gGlobalMutex;
256 }
257
258 if(*mutex == NULL) {
259#if (ICU_USE_THREADS == 1)
260 U_ASSERT(FALSE); /* This mutex is not initialized. */
261#endif
262 return;
263 }
264
265#if defined (U_WINDOWS) && defined (U_DEBUG) && (ICU_USE_THREADS==1)
266 if (mutex == &gGlobalMutex) {
267 gRecursionCount--;
268 U_ASSERT(gRecursionCount == 0); /* Detect unlock of an already unlocked mutex */
269 }
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];
275
276 U_ASSERT(gRecursionCountPool[i] == 0); /* !Detect Deadlock! */
277
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);
284 */
285 }
286#endif
287
288 if (pMutexUnlockFn) {
289 (*pMutexUnlockFn)(gMutexContext, mutex);
290 } else {
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 */
298 }
299}
300
301
302
303
304/*
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.
309 */
310static void initGlobalMutex() {
311 /*
312 * If User Supplied mutex functions are in use
313 * init the icu global mutex using them.
314 */
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? */
321 return;
322 }
323 }
324 return;
325 }
326
327 /* No user override of mutex functions.
328 * Use default ICU mutex implementations.
329 */
330#if (ICU_USE_THREADS == 1)
331 /*
332 * for Windows, init the pool of critical sections that we
333 * will use as needed for ICU mutexes.
334 */
335#if defined (U_WINDOWS)
336 if (gMutexPoolInitialized == FALSE) {
337 int i;
338 for (i=0; i<MAX_MUTEXES; i++) {
339 InitializeCriticalSection(&gMutexes[i]);
340#if defined (U_DEBUG)
341 gRecursionCountPool[i] = 0; /* see comments above */
342#endif
343 }
344 gMutexPoolInitialized = TRUE;
345 }
346#elif defined (POSIX)
347 /* TODO: experimental code. Shouldn't need to explicitly init the mutexes. */
348 if (gMutexPoolInitialized == FALSE) {
349 int i;
350 for (i=0; i<MAX_MUTEXES; i++) {
351 pthread_mutex_init(&gMutexes[i], NULL);
352 }
353 gMutexPoolInitialized = TRUE;
354 }
355#endif
356
357 /*
358 * for both Windows & POSIX, the first mutex in the array is used
359 * for the ICU global mutex.
360 */
361 gGlobalMutex = &gMutexes[0];
362 gMutexesInUse[0] = 1;
363
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.
368 */
369#endif /* ICU_USE_THREADS */
370}
371
372
373
374
375
376U_CAPI void U_EXPORT2
377umtx_init(UMTX *mutex)
378{
379 if (mutex == NULL || mutex == &gGlobalMutex) {
380 initGlobalMutex();
381 } else {
382 umtx_lock(NULL);
383 if (*mutex != NULL) {
384 /* Another thread initialized this mutex first. */
385 umtx_unlock(NULL);
386 return;
387 }
388
389 if (pMutexInitFn != NULL) {
390 UErrorCode status = U_ZERO_ERROR;
391 (*pMutexInitFn)(gMutexContext, mutex, &status);
392 /* TODO: how to report failure on init? */
393 umtx_unlock(NULL);
394 return;
395 }
396 else {
397#if (ICU_USE_THREADS == 1)
398 /* Search through our pool of pre-allocated mutexes for one that is not
399 * already in use. */
400 int i;
401 for (i=0; i<MAX_MUTEXES; i++) {
402 if (gMutexesInUse[i] == 0) {
403 gMutexesInUse[i] = 1;
404 *mutex = &gMutexes[i];
405 break;
406 }
407 }
408#endif
409 }
410 umtx_unlock(NULL);
411
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);
416#endif
417 }
418}
419
420
421/*
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.
427 */
428U_CAPI void U_EXPORT2
429umtx_destroy(UMTX *mutex) {
430 if (mutex == NULL) { /* destroy the global mutex */
431 mutex = &gGlobalMutex;
432 }
433
434 if (*mutex == NULL) { /* someone already did it. */
435 return;
436 }
437
438 /* The life of the inc/dec mutex is tied to that of the global mutex. */
439 if (mutex == &gGlobalMutex) {
440 umtx_destroy(&gIncDecMutex);
441 }
442
443 if (pMutexDestroyFn != NULL) {
444 /* Mutexes are being managed by the app. Call back to it for the destroy. */
445 (*pMutexDestroyFn)(gMutexContext, mutex);
446 }
447 else {
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.
451 */
452 /* TODO use pointer math here, instead of iterating! */
453 int i;
454 for (i=0; i<MAX_MUTEXES; i++) {
455 if (*mutex == &gMutexes[i]) {
456 gMutexesInUse[i] = 0;
457 break;
458 }
459 }
460#endif
461 }
462
463 *mutex = NULL;
464}
465
466
467
468U_CAPI void U_EXPORT2
469u_setMutexFunctions(const void *context, UMtxInitFn *i, UMtxFn *d, UMtxFn *l, UMtxFn *u,
470 UErrorCode *status) {
471 if (U_FAILURE(*status)) {
472 return;
473 }
474
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;
478 return;
479 }
480
481 /* If ICU is not in an initial state, disallow this operation. */
482 if (cmemory_inUse()) {
483 *status = U_INVALID_STATE_ERROR;
484 return;
485 }
486
487 /* Swap in the mutex function pointers. */
488 pMutexInitFn = i;
489 pMutexDestroyFn = d;
490 pMutexLockFn = l;
491 pMutexUnlockFn = u;
492 gMutexContext = context;
493 gGlobalMutex = NULL; /* For POSIX, the global mutex will be pre-initialized */
494 /* Undo that, force re-initialization when u_init() */
495 /* happens. */
496}
497
498
499
500/*-----------------------------------------------------------------
501 *
502 * Atomic Increment and Decrement
503 * umtx_atomic_inc
504 * umtx_atomic_dec
505 *
506 *----------------------------------------------------------------*/
507
508/* Pointers to user-supplied inc/dec functions. Null if no funcs have been set. */
509static UMtxAtomicFn *pIncFn = NULL;
510static UMtxAtomicFn *pDecFn = NULL;
511static const void *gIncDecContext = NULL;
512
513
514U_CAPI int32_t U_EXPORT2
515umtx_atomic_inc(int32_t *p) {
516 int32_t retVal;
517 if (pIncFn) {
518 retVal = (*pIncFn)(gIncDecContext, p);
519 } else {
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);
526 retVal = ++(*p);
527 umtx_unlock(&gIncDecMutex);
528 #else
529 /* Unknown Platform, or ICU thread support compiled out. */
530 retVal = ++(*p);
531 #endif
532 }
533 return retVal;
534}
535
536U_CAPI int32_t U_EXPORT2
537umtx_atomic_dec(int32_t *p) {
538 int32_t retVal;
539 if (pDecFn) {
540 retVal = (*pDecFn)(gIncDecContext, p);
541 } else {
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);
548 retVal = --(*p);
549 umtx_unlock(&gIncDecMutex);
550 #else
551 /* Unknown Platform, or ICU thread support compiled out. */
552 retVal = --(*p);
553 #endif
554 }
555 return retVal;
556}
557
558/* TODO: Some POSIXy platforms have atomic inc/dec functions available. Use them. */
559
560
561
562
563
564U_CAPI void U_EXPORT2
565u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *ip, UMtxAtomicFn *dp,
566 UErrorCode *status) {
567 if (U_FAILURE(*status)) {
568 return;
569 }
570 /* Can not set a mutex function to a NULL value */
571 if (ip==NULL || dp==NULL) {
572 *status = U_ILLEGAL_ARGUMENT_ERROR;
573 return;
574 }
575 /* If ICU is not in an initial state, disallow this operation. */
576 if (cmemory_inUse()) {
577 *status = U_INVALID_STATE_ERROR;
578 return;
579 }
580
581 pIncFn = ip;
582 pDecFn = dp;
583 gIncDecContext = context;
584
585#if !U_RELEASE
586 {
587 int32_t testInt = 0;
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);
592 }
593#endif
594}
595
596
597
598/*
599 * Mutex Cleanup Function
600 *
601 * Destroy the global mutex(es), and reset the mutex function callback pointers.
602 */
603U_CFUNC UBool umtx_cleanup(void) {
604 umtx_destroy(NULL);
605 pMutexInitFn = NULL;
606 pMutexDestroyFn = NULL;
607 pMutexLockFn = NULL;
608 pMutexUnlockFn = NULL;
609 gMutexContext = NULL;
610 gGlobalMutex = NULL;
611 pIncFn = NULL;
612 pDecFn = NULL;
613 gIncDecContext = NULL;
614 gIncDecMutex = NULL;
615
616#if (ICU_USE_THREADS == 1)
617 if (gMutexPoolInitialized) {
618 int i;
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]);
625#endif
626 gMutexesInUse[i] = 0;
627 }
628 }
629 }
630 gMutexPoolInitialized = FALSE;
631#endif
632
633 return TRUE;
634}
635
636