]> git.saurik.com Git - apple/icu.git/blame - icuSources/common/umutex.cpp
ICU-511.25.tar.gz
[apple/icu.git] / icuSources / common / umutex.cpp
CommitLineData
51004dcb
A
1/*
2******************************************************************************
3*
4* Copyright (C) 1997-2012, International Business Machines
5* Corporation and others. All Rights Reserved.
6*
7******************************************************************************
8*
9* File umutex.cpp
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 U_PLATFORM_HAS_WIN32_API
31 /* Prefer native Windows APIs even if POSIX is implemented (i.e., on Cygwin). */
32# undef POSIX
33#elif U_PLATFORM_IMPLEMENTS_POSIX
34# define POSIX
35#else
36# undef POSIX
37#endif
38
39#if defined(POSIX)
40# include <pthread.h> /* must be first, so that we get the multithread versions of things. */
41#endif /* POSIX */
42
43#if U_PLATFORM_HAS_WIN32_API
44# define WIN32_LEAN_AND_MEAN
45# define VC_EXTRALEAN
46# define NOUSER
47# define NOSERVICE
48# define NOIME
49# define NOMCX
50# include <windows.h>
51#endif
52
53#include "umutex.h"
54#include "cmemory.h"
55
56#if U_PLATFORM_HAS_WIN32_API
57#define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
58 InterlockedCompareExchangePointer(dest, newval, oldval)
59
60#elif defined(POSIX)
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)
64#else
65#define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
66 mutexed_compare_and_swap(dest, newval, oldval)
67#endif
68
69#else
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)
73#endif
74
75static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval);
76
77// The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
78static UMutex globalMutex = U_MUTEX_INITIALIZER;
79
80// Implementation mutex. Used for compare & swap when no intrinsic is available, and
81// for safe initialization of user defined mutexes.
82static UMutex implMutex = U_MUTEX_INITIALIZER;
83
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.
88//
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.
92//
93static const int MUTEX_LIST_LIMIT = 100;
94static UMutex *gMutexList[MUTEX_LIST_LIMIT];
95static int gMutexListSize = 0;
96
97
98/*
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)
102 */
103static UMtxInitFn *pMutexInitFn = NULL;
104static UMtxFn *pMutexDestroyFn = NULL;
105static UMtxFn *pMutexLockFn = NULL;
106static UMtxFn *pMutexUnlockFn = NULL;
107static const void *gMutexContext = NULL;
108
109
110// Clean up (undo) the effects of u_setMutexFunctions().
111//
112static 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;
119 }
120 (*pMutexDestroyFn)(gMutexContext, &globalMutex.fUserMutex);
121 (*pMutexDestroyFn)(gMutexContext, &implMutex.fUserMutex);
122 }
123 gMutexListSize = 0;
124 pMutexInitFn = NULL;
125 pMutexDestroyFn = NULL;
126 pMutexLockFn = NULL;
127 pMutexUnlockFn = NULL;
128 gMutexContext = NULL;
129}
130
131
132/*
133 * User mutex lock.
134 *
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.
138 *
139 */
140static 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;
151 ++gMutexListSize;
152 }
153 }
154 umtx_unlock(&implMutex);
155 }
156 (*pMutexLockFn)(gMutexContext, &mutex->fUserMutex);
157}
158
159
160
161#if defined(POSIX)
162
163//
164// POSIX implementation of UMutex.
165//
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.
169
170U_CAPI void U_EXPORT2
171umtx_lock(UMutex *mutex) {
172 if (mutex == NULL) {
173 mutex = &globalMutex;
174 }
175 if (pMutexLockFn) {
176 usrMutexLock(mutex);
177 } else {
178 #if U_DEBUG
179 // #if to avoid unused variable warnings in non-debug builds.
180 int sysErr = pthread_mutex_lock(&mutex->fMutex);
181 U_ASSERT(sysErr == 0);
182 #else
183 pthread_mutex_lock(&mutex->fMutex);
184 #endif
185 }
186}
187
188
189U_CAPI void U_EXPORT2
190umtx_unlock(UMutex* mutex)
191{
192 if (mutex == NULL) {
193 mutex = &globalMutex;
194 }
195 if (pMutexUnlockFn) {
196 (*pMutexUnlockFn)(gMutexContext, &mutex->fUserMutex);
197 } else {
198 #if U_DEBUG
199 // #if to avoid unused variable warnings in non-debug builds.
200 int sysErr = pthread_mutex_unlock(&mutex->fMutex);
201 U_ASSERT(sysErr == 0);
202 #else
203 pthread_mutex_unlock(&mutex->fMutex);
204 #endif
205 }
206}
207
208#elif U_PLATFORM_HAS_WIN32_API
209//
210// Windows implementation of UMutex.
211//
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.
215//
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.
219
220typedef UBool (*U_PINIT_ONCE_FN) (
221 U_INIT_ONCE *initOnce,
222 void *parameter,
223 void **context
224);
225
226UBool u_InitOnceExecuteOnce(
227 U_INIT_ONCE *initOnce,
228 U_PINIT_ONCE_FN initFn,
229 void *parameter,
230 void **context) {
231 for (;;) {
232 long previousState = InterlockedCompareExchange(
233 &initOnce->fState, // Destination,
234 1, // Exchange Value
235 0); // Compare value
236 if (previousState == 2) {
237 // Initialization was already completed.
238 if (context != NULL) {
239 *context = initOnce->fContext;
240 }
241 return TRUE;
242 }
243 if (previousState == 1) {
244 // Initialization is in progress in some other thread.
245 // Loop until it completes.
246 Sleep(1);
247 continue;
248 }
249
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.
255
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.
262 }
263};
264
265static 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);
269 return TRUE;
270}
271
272/*
273 * umtx_lock
274 */
275U_CAPI void U_EXPORT2
276umtx_lock(UMutex *mutex) {
277 if (mutex == NULL) {
278 mutex = &globalMutex;
279 }
280 if (pMutexLockFn) {
281 usrMutexLock(mutex);
282 } else {
283 u_InitOnceExecuteOnce(&mutex->fInitOnce, winMutexInit, mutex, NULL);
284 EnterCriticalSection((CRITICAL_SECTION *)mutex->fCS);
285 }
286}
287
288U_CAPI void U_EXPORT2
289umtx_unlock(UMutex* mutex)
290{
291 if (mutex == NULL) {
292 mutex = &globalMutex;
293 }
294 if (pMutexUnlockFn) {
295 (*pMutexUnlockFn)(gMutexContext, &mutex->fUserMutex);
296 } else {
297 LeaveCriticalSection((CRITICAL_SECTION *)mutex->fCS);
298 }
299}
300
301#endif // Windows Implementation
302
303
304U_CAPI void U_EXPORT2
305u_setMutexFunctions(const void *context, UMtxInitFn *i, UMtxFn *d, UMtxFn *l, UMtxFn *u,
306 UErrorCode *status) {
307 if (U_FAILURE(*status)) {
308 return;
309 }
310
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;
314 return;
315 }
316
317 /* If ICU is not in an initial state, disallow this operation. */
318 if (cmemory_inUse()) {
319 *status = U_INVALID_STATE_ERROR;
320 return;
321 }
322
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.
328
329 usrMutexCleanup();
330
331 /* Swap in the mutex function pointers. */
332 pMutexInitFn = i;
333 pMutexDestroyFn = d;
334 pMutexLockFn = l;
335 pMutexUnlockFn = u;
336 gMutexContext = context;
337 gMutexListSize = 0;
338
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.
341 */
342 (*pMutexInitFn)(gMutexContext, &globalMutex.fUserMutex, status);
343 globalMutex.fInitialized = TRUE;
344 (*pMutexInitFn)(gMutexContext, &implMutex.fUserMutex, status);
345 implMutex.fInitialized = TRUE;
346}
347
348
349
350/* synchronized compare and swap function, for use when OS or compiler built-in
351 * equivalents aren't available.
352 */
353static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval) {
354 umtx_lock(&implMutex);
355 void *temp = *dest;
356 if (temp == oldval) {
357 *dest = newval;
358 }
359 umtx_unlock(&implMutex);
360
361 return temp;
362}
363
364
365
366/*-----------------------------------------------------------------
367 *
368 * Atomic Increment and Decrement
369 * umtx_atomic_inc
370 * umtx_atomic_dec
371 *
372 *----------------------------------------------------------------*/
373
374/* Pointers to user-supplied inc/dec functions. Null if no funcs have been set. */
375static UMtxAtomicFn *pIncFn = NULL;
376static UMtxAtomicFn *pDecFn = NULL;
377static const void *gIncDecContext = NULL;
378
379#if defined (POSIX) && (U_HAVE_GCC_ATOMICS == 0)
380static UMutex gIncDecMutex = U_MUTEX_INITIALIZER;
381#endif
382
383U_CAPI int32_t U_EXPORT2
384umtx_atomic_inc(int32_t *p) {
385 int32_t retVal;
386 if (pIncFn) {
387 retVal = (*pIncFn)(gIncDecContext, p);
388 } else {
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);
397 retVal = ++(*p);
398 umtx_unlock(&gIncDecMutex);
399 #else
400 /* Unknown Platform. */
401 retVal = ++(*p);
402 #endif
403 }
404 return retVal;
405}
406
407U_CAPI int32_t U_EXPORT2
408umtx_atomic_dec(int32_t *p) {
409 int32_t retVal;
410 if (pDecFn) {
411 retVal = (*pDecFn)(gIncDecContext, p);
412 } else {
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);
421 retVal = --(*p);
422 umtx_unlock(&gIncDecMutex);
423 #else
424 /* Unknown Platform. */
425 retVal = --(*p);
426 #endif
427 }
428 return retVal;
429}
430
431
432
433U_CAPI void U_EXPORT2
434u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *ip, UMtxAtomicFn *dp,
435 UErrorCode *status) {
436 if (U_FAILURE(*status)) {
437 return;
438 }
439 /* Can not set a mutex function to a NULL value */
440 if (ip==NULL || dp==NULL) {
441 *status = U_ILLEGAL_ARGUMENT_ERROR;
442 return;
443 }
444 /* If ICU is not in an initial state, disallow this operation. */
445 if (cmemory_inUse()) {
446 *status = U_INVALID_STATE_ERROR;
447 return;
448 }
449
450 pIncFn = ip;
451 pDecFn = dp;
452 gIncDecContext = context;
453
454#if U_DEBUG
455 {
456 int32_t testInt = 0;
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);
461 }
462#endif
463}
464
465
466/*
467 * Mutex Cleanup Function
468 * Reset the mutex function callback pointers.
469 * Called from the global ICU u_cleanup() function.
470 */
471U_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);
476 usrMutexCleanup();
477
478 pIncFn = NULL;
479 pDecFn = NULL;
480 gIncDecContext = NULL;
481
482 return TRUE;
483}