]>
Commit | Line | Data |
---|---|---|
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 | |
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]; | |
99 | ||
100 | #if defined(U_WINDOWS) | |
101 | /*------------------------------------------------------------- | |
102 | * | |
103 | * WINDOWS platform variable declarations | |
104 | * | |
105 | *-------------------------------------------------------------*/ | |
106 | static CRITICAL_SECTION gMutexes[MAX_MUTEXES]; | |
107 | static 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) | |
134 | static int32_t gRecursionCount = 0; /* detect global mutex locking */ | |
135 | static 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 | *-------------------------------------------------------------*/ | |
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 | |
153 | }; | |
154 | ||
155 | #else | |
156 | /*------------------------------------------------------------- | |
157 | * | |
158 | * UNKNOWN platform declarations | |
159 | * | |
160 | *-------------------------------------------------------------*/ | |
161 | static 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 | */ | |
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; | |
192 | ||
193 | ||
194 | ||
195 | /* | |
196 | * umtx_lock | |
197 | */ | |
198 | U_CAPI void U_EXPORT2 | |
199 | umtx_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 | */ | |
251 | U_CAPI void U_EXPORT2 | |
252 | umtx_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 | */ | |
310 | static 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 | ||
376 | U_CAPI void U_EXPORT2 | |
377 | umtx_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 | */ | |
428 | U_CAPI void U_EXPORT2 | |
429 | umtx_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 | ||
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)) { | |
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. */ | |
509 | static UMtxAtomicFn *pIncFn = NULL; | |
510 | static UMtxAtomicFn *pDecFn = NULL; | |
511 | static const void *gIncDecContext = NULL; | |
512 | ||
513 | ||
514 | U_CAPI int32_t U_EXPORT2 | |
515 | umtx_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 | ||
536 | U_CAPI int32_t U_EXPORT2 | |
537 | umtx_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 | ||
564 | U_CAPI void U_EXPORT2 | |
565 | u_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 | */ | |
603 | U_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 |