]>
git.saurik.com Git - apple/icu.git/blob - icuSources/common/umutex.cpp
2 ******************************************************************************
4 * Copyright (C) 1997-2012, 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 * 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.
30 #if U_PLATFORM_HAS_WIN32_API
31 /* Prefer native Windows APIs even if POSIX is implemented (i.e., on Cygwin). */
33 #elif U_PLATFORM_IMPLEMENTS_POSIX
40 # include <pthread.h> /* must be first, so that we get the multithread versions of things. */
43 #if U_PLATFORM_HAS_WIN32_API
44 # define WIN32_LEAN_AND_MEAN
56 #if U_PLATFORM_HAS_WIN32_API
57 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
58 InterlockedCompareExchangePointer(dest, newval, oldval)
61 #if (U_HAVE_GCC_ATOMICS == 1)
62 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
63 __sync_val_compare_and_swap(dest, oldval, newval)
65 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
66 mutexed_compare_and_swap(dest, newval, oldval)
70 // Unknown platform. Note that user can still set mutex functions at run time.
71 #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
72 mutexed_compare_and_swap(dest, newval, oldval)
75 static void *mutexed_compare_and_swap(void **dest
, void *newval
, void *oldval
);
77 // The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
78 static UMutex globalMutex
= U_MUTEX_INITIALIZER
;
80 // Implementation mutex. Used for compare & swap when no intrinsic is available, and
81 // for safe initialization of user defined mutexes.
82 static UMutex implMutex
= U_MUTEX_INITIALIZER
;
84 // List of all user mutexes that have been initialized.
85 // Used to allow us to destroy them when cleaning up ICU.
86 // Normal platform mutexes are not kept track of in this way - they survive until the process is shut down.
87 // Normal platfrom mutexes don't allocate storage, so not cleaning them up won't trigger memory leak complaints.
89 // Note: putting this list in allocated memory would be awkward to arrange, because memory allocations
90 // are used as a flag to indicate that ICU has been initialized, and setting other ICU
91 // override functions will no longer work.
93 static const int MUTEX_LIST_LIMIT
= 100;
94 static UMutex
*gMutexList
[MUTEX_LIST_LIMIT
];
95 static int gMutexListSize
= 0;
99 * User mutex implementation functions. If non-null, call back to these rather than
100 * directly using the system (Posix or Windows) APIs. See u_setMutexFunctions().
101 * (declarations are in uclean.h)
103 static UMtxInitFn
*pMutexInitFn
= NULL
;
104 static UMtxFn
*pMutexDestroyFn
= NULL
;
105 static UMtxFn
*pMutexLockFn
= NULL
;
106 static UMtxFn
*pMutexUnlockFn
= NULL
;
107 static const void *gMutexContext
= NULL
;
110 // Clean up (undo) the effects of u_setMutexFunctions().
112 static void usrMutexCleanup() {
113 if (pMutexDestroyFn
!= NULL
) {
114 for (int i
= 0; i
< gMutexListSize
; i
++) {
115 UMutex
*m
= gMutexList
[i
];
116 U_ASSERT(m
->fInitialized
);
117 (*pMutexDestroyFn
)(gMutexContext
, &m
->fUserMutex
);
118 m
->fInitialized
= FALSE
;
120 (*pMutexDestroyFn
)(gMutexContext
, &globalMutex
.fUserMutex
);
121 (*pMutexDestroyFn
)(gMutexContext
, &implMutex
.fUserMutex
);
125 pMutexDestroyFn
= NULL
;
127 pMutexUnlockFn
= NULL
;
128 gMutexContext
= NULL
;
135 * User mutexes need to be initialized before they can be used. We use the impl mutex
136 * to synchronize the initialization check. This could be sped up on platforms that
137 * support alternate ways to safely check the initialization flag.
140 static void usrMutexLock(UMutex
*mutex
) {
141 UErrorCode status
= U_ZERO_ERROR
;
142 if (!(mutex
== &implMutex
|| mutex
== &globalMutex
)) {
143 umtx_lock(&implMutex
);
144 if (!mutex
->fInitialized
) {
145 (*pMutexInitFn
)(gMutexContext
, &mutex
->fUserMutex
, &status
);
146 U_ASSERT(U_SUCCESS(status
));
147 mutex
->fInitialized
= TRUE
;
148 U_ASSERT(gMutexListSize
< MUTEX_LIST_LIMIT
);
149 if (gMutexListSize
< MUTEX_LIST_LIMIT
) {
150 gMutexList
[gMutexListSize
] = mutex
;
154 umtx_unlock(&implMutex
);
156 (*pMutexLockFn
)(gMutexContext
, &mutex
->fUserMutex
);
164 // POSIX implementation of UMutex.
166 // Each UMutex has a corresponding pthread_mutex_t.
167 // All are statically initialized and ready for use.
168 // There is no runtime mutex initialization code needed.
170 U_CAPI
void U_EXPORT2
171 umtx_lock(UMutex
*mutex
) {
173 mutex
= &globalMutex
;
179 // #if to avoid unused variable warnings in non-debug builds.
180 int sysErr
= pthread_mutex_lock(&mutex
->fMutex
);
181 U_ASSERT(sysErr
== 0);
183 pthread_mutex_lock(&mutex
->fMutex
);
189 U_CAPI
void U_EXPORT2
190 umtx_unlock(UMutex
* mutex
)
193 mutex
= &globalMutex
;
195 if (pMutexUnlockFn
) {
196 (*pMutexUnlockFn
)(gMutexContext
, &mutex
->fUserMutex
);
199 // #if to avoid unused variable warnings in non-debug builds.
200 int sysErr
= pthread_mutex_unlock(&mutex
->fMutex
);
201 U_ASSERT(sysErr
== 0);
203 pthread_mutex_unlock(&mutex
->fMutex
);
208 #elif U_PLATFORM_HAS_WIN32_API
210 // Windows implementation of UMutex.
212 // Each UMutex has a corresponding Windows CRITICAL_SECTION.
213 // CRITICAL_SECTIONS must be initialized before use. This is done
214 // with a InitOnceExcuteOnce operation.
216 // InitOnceExecuteOnce was introduced with Windows Vista. For now ICU
217 // must support Windows XP, so we roll our own. ICU will switch to the
218 // native Windows InitOnceExecuteOnce when possible.
220 typedef UBool (*U_PINIT_ONCE_FN
) (
221 U_INIT_ONCE
*initOnce
,
226 UBool
u_InitOnceExecuteOnce(
227 U_INIT_ONCE
*initOnce
,
228 U_PINIT_ONCE_FN initFn
,
232 long previousState
= InterlockedCompareExchange(
233 &initOnce
->fState
, // Destination,
236 if (previousState
== 2) {
237 // Initialization was already completed.
238 if (context
!= NULL
) {
239 *context
= initOnce
->fContext
;
243 if (previousState
== 1) {
244 // Initialization is in progress in some other thread.
245 // Loop until it completes.
250 // Initialization needed. Execute the callback function to do it.
251 U_ASSERT(previousState
== 0);
252 U_ASSERT(initOnce
->fState
== 1);
253 UBool success
= (*initFn
)(initOnce
, parameter
, &initOnce
->fContext
);
254 U_ASSERT(success
); // ICU is not supporting the failure case.
256 // Assign the state indicating that initialization has completed.
257 // Using InterlockedCompareExchange to do it ensures that all
258 // threads will have a consistent view of memory.
259 previousState
= InterlockedCompareExchange(&initOnce
->fState
, 2, 1);
260 U_ASSERT(previousState
== 1);
261 // Next loop iteration will see the initialization and return.
265 static UBool
winMutexInit(U_INIT_ONCE
*initOnce
, void *param
, void **context
) {
266 UMutex
*mutex
= static_cast<UMutex
*>(param
);
267 U_ASSERT(sizeof(CRITICAL_SECTION
) <= sizeof(mutex
->fCS
));
268 InitializeCriticalSection((CRITICAL_SECTION
*)mutex
->fCS
);
275 U_CAPI
void U_EXPORT2
276 umtx_lock(UMutex
*mutex
) {
278 mutex
= &globalMutex
;
283 u_InitOnceExecuteOnce(&mutex
->fInitOnce
, winMutexInit
, mutex
, NULL
);
284 EnterCriticalSection((CRITICAL_SECTION
*)mutex
->fCS
);
288 U_CAPI
void U_EXPORT2
289 umtx_unlock(UMutex
* mutex
)
292 mutex
= &globalMutex
;
294 if (pMutexUnlockFn
) {
295 (*pMutexUnlockFn
)(gMutexContext
, &mutex
->fUserMutex
);
297 LeaveCriticalSection((CRITICAL_SECTION
*)mutex
->fCS
);
301 #endif // Windows Implementation
304 U_CAPI
void U_EXPORT2
305 u_setMutexFunctions(const void *context
, UMtxInitFn
*i
, UMtxFn
*d
, UMtxFn
*l
, UMtxFn
*u
,
306 UErrorCode
*status
) {
307 if (U_FAILURE(*status
)) {
311 /* Can not set a mutex function to a NULL value */
312 if (i
==NULL
|| d
==NULL
|| l
==NULL
|| u
==NULL
) {
313 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
317 /* If ICU is not in an initial state, disallow this operation. */
318 if (cmemory_inUse()) {
319 *status
= U_INVALID_STATE_ERROR
;
323 // Clean up any previously set user mutex functions.
324 // It's possible to call u_setMutexFunctions() more than once without without explicitly cleaning up,
325 // and the last call should take. Kind of a corner case, but it worked once, there is a test for
326 // it, so we keep it working. The global and impl mutexes will have been created by the
327 // previous u_setMutexFunctions(), and now need to be destroyed.
331 /* Swap in the mutex function pointers. */
336 gMutexContext
= context
;
339 /* Initialize the global and impl mutexes. Safe to do at this point because
340 * u_setMutexFunctions must be done in a single-threaded envioronment. Not thread safe.
342 (*pMutexInitFn
)(gMutexContext
, &globalMutex
.fUserMutex
, status
);
343 globalMutex
.fInitialized
= TRUE
;
344 (*pMutexInitFn
)(gMutexContext
, &implMutex
.fUserMutex
, status
);
345 implMutex
.fInitialized
= TRUE
;
350 /* synchronized compare and swap function, for use when OS or compiler built-in
351 * equivalents aren't available.
353 static void *mutexed_compare_and_swap(void **dest
, void *newval
, void *oldval
) {
354 umtx_lock(&implMutex
);
356 if (temp
== oldval
) {
359 umtx_unlock(&implMutex
);
366 /*-----------------------------------------------------------------
368 * Atomic Increment and Decrement
372 *----------------------------------------------------------------*/
374 /* Pointers to user-supplied inc/dec functions. Null if no funcs have been set. */
375 static UMtxAtomicFn
*pIncFn
= NULL
;
376 static UMtxAtomicFn
*pDecFn
= NULL
;
377 static const void *gIncDecContext
= NULL
;
379 #if defined (POSIX) && (U_HAVE_GCC_ATOMICS == 0)
380 static UMutex gIncDecMutex
= U_MUTEX_INITIALIZER
;
383 U_CAPI
int32_t U_EXPORT2
384 umtx_atomic_inc(int32_t *p
) {
387 retVal
= (*pIncFn
)(gIncDecContext
, p
);
389 #if U_PLATFORM_HAS_WIN32_API
390 retVal
= InterlockedIncrement((LONG
*)p
);
391 #elif defined(USE_MAC_OS_ATOMIC_INCREMENT)
392 retVal
= OSAtomicIncrement32Barrier(p
);
393 #elif (U_HAVE_GCC_ATOMICS == 1)
394 retVal
= __sync_add_and_fetch(p
, 1);
395 #elif defined (POSIX)
396 umtx_lock(&gIncDecMutex
);
398 umtx_unlock(&gIncDecMutex
);
400 /* Unknown Platform. */
407 U_CAPI
int32_t U_EXPORT2
408 umtx_atomic_dec(int32_t *p
) {
411 retVal
= (*pDecFn
)(gIncDecContext
, p
);
413 #if U_PLATFORM_HAS_WIN32_API
414 retVal
= InterlockedDecrement((LONG
*)p
);
415 #elif defined(USE_MAC_OS_ATOMIC_INCREMENT)
416 retVal
= OSAtomicDecrement32Barrier(p
);
417 #elif (U_HAVE_GCC_ATOMICS == 1)
418 retVal
= __sync_sub_and_fetch(p
, 1);
419 #elif defined (POSIX)
420 umtx_lock(&gIncDecMutex
);
422 umtx_unlock(&gIncDecMutex
);
424 /* Unknown Platform. */
433 U_CAPI
void U_EXPORT2
434 u_setAtomicIncDecFunctions(const void *context
, UMtxAtomicFn
*ip
, UMtxAtomicFn
*dp
,
435 UErrorCode
*status
) {
436 if (U_FAILURE(*status
)) {
439 /* Can not set a mutex function to a NULL value */
440 if (ip
==NULL
|| dp
==NULL
) {
441 *status
= U_ILLEGAL_ARGUMENT_ERROR
;
444 /* If ICU is not in an initial state, disallow this operation. */
445 if (cmemory_inUse()) {
446 *status
= U_INVALID_STATE_ERROR
;
452 gIncDecContext
= context
;
457 U_ASSERT(umtx_atomic_inc(&testInt
) == 1); /* Sanity Check. Do the functions work at all? */
458 U_ASSERT(testInt
== 1);
459 U_ASSERT(umtx_atomic_dec(&testInt
) == 0);
460 U_ASSERT(testInt
== 0);
467 * Mutex Cleanup Function
468 * Reset the mutex function callback pointers.
469 * Called from the global ICU u_cleanup() function.
471 U_CFUNC UBool
umtx_cleanup(void) {
472 /* Extra, do-nothing function call to suppress compiler warnings on platforms where
473 * mutexed_compare_and_swap is not otherwise used. */
474 void *pv
= &globalMutex
;
475 mutexed_compare_and_swap(&pv
, NULL
, NULL
);
480 gIncDecContext
= NULL
;