]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | ****************************************************************************** | |
3 | * | |
4 | * Copyright (C) 1997-2009, International Business Machines | |
5 | * Corporation and others. All Rights Reserved. | |
6 | * | |
7 | ****************************************************************************** | |
8 | * | |
9 | * File umutex.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 | /* | |
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 defined(U_DARWIN) | |
31 | #include <AvailabilityMacros.h> | |
32 | #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) | |
33 | #if defined(__STRICT_ANSI__) | |
34 | #define UPRV_REMAP_INLINE | |
35 | #define inline | |
36 | #endif | |
37 | #include <libkern/OSAtomic.h> | |
38 | #define USE_MAC_OS_ATOMIC_INCREMENT 1 | |
39 | #if defined(UPRV_REMAP_INLINE) | |
40 | #undef inline | |
41 | #undef UPRV_REMAP_INLINE | |
42 | #endif | |
43 | #endif | |
44 | #endif | |
45 | ||
46 | /* Assume POSIX, and modify as necessary below */ | |
47 | #define POSIX | |
48 | ||
49 | #if defined(U_WINDOWS) | |
50 | #undef POSIX | |
51 | #endif | |
52 | #if defined(macintosh) | |
53 | #undef POSIX | |
54 | #endif | |
55 | #if defined(OS2) | |
56 | #undef POSIX | |
57 | #endif | |
58 | ||
59 | #if defined(POSIX) && (ICU_USE_THREADS==1) | |
60 | # include <pthread.h> /* must be first, so that we get the multithread versions of things. */ | |
61 | ||
62 | #endif /* POSIX && (ICU_USE_THREADS==1) */ | |
63 | ||
64 | #ifdef U_WINDOWS | |
65 | # define WIN32_LEAN_AND_MEAN | |
66 | # define VC_EXTRALEAN | |
67 | # define NOUSER | |
68 | # define NOSERVICE | |
69 | # define NOIME | |
70 | # define NOMCX | |
71 | # include <windows.h> | |
72 | #endif | |
73 | ||
74 | #include "umutex.h" | |
75 | #include "cmemory.h" | |
76 | ||
77 | /* | |
78 | * A note on ICU Mutex Initialization and ICU startup: | |
79 | * | |
80 | * ICU mutexes, as used through the rest of the ICU code, are self-initializing. | |
81 | * To make this work, ICU uses the _ICU GLobal Mutex_ to synchronize the lazy init | |
82 | * of other ICU mutexes. For the global mutex itself, we need some other mechanism | |
83 | * to safely initialize it on first use. This becomes important when two or more | |
84 | * threads are more or less simultaenously the first to use ICU in a process, and | |
85 | * are racing into the mutex initialization code. | |
86 | * | |
87 | * | |
88 | * The solution for the global mutex init is platform dependent. | |
89 | * On POSIX systems, plain C-style initialization can be used on a mutex, with the | |
90 | * macro PTHREAD_MUTEX_INITIALIZER. The mutex is then ready for use, without | |
91 | * first calling pthread_mutex_init(). | |
92 | * | |
93 | * Windows has no equivalent statically initialized mutex or CRITICAL SECION. | |
94 | * InitializeCriticalSection() must be called. If the global mutex does not | |
95 | * appear to be initialized, a thread will create and initialize a new | |
96 | * CRITICAL_SECTION, then use a Windows InterlockedCompareAndExchange to | |
97 | * swap it in as the global mutex while avoid problems with race conditions. | |
98 | */ | |
99 | ||
100 | /* On WIN32 mutexes are reentrant. On POSIX platforms they are not, and a deadlock | |
101 | * will occur if a thread attempts to acquire a mutex it already has locked. | |
102 | * ICU mutexes (in debug builds) include checking code that will cause an assertion | |
103 | * failure if a mutex is reentered. If you are having deadlock problems | |
104 | * on a POSIX machine, debugging may be easier on Windows. | |
105 | */ | |
106 | ||
107 | ||
108 | #if (ICU_USE_THREADS == 0) | |
109 | #define MUTEX_TYPE void * | |
110 | #define PLATFORM_MUTEX_INIT(m) | |
111 | #define PLATFORM_MUTEX_LOCK(m) | |
112 | #define PLATFORM_MUTEX_UNLOCK(m) | |
113 | #define PLATFORM_MUTEX_DESTROY(m) | |
114 | #define PLATFORM_MUTEX_INITIALIZER NULL | |
115 | #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ | |
116 | mutexed_compare_and_swap(dest, newval, oldval) | |
117 | ||
118 | ||
119 | #elif defined(U_WINDOWS) | |
120 | #define MUTEX_TYPE CRITICAL_SECTION | |
121 | #define PLATFORM_MUTEX_INIT(m) InitializeCriticalSection(m) | |
122 | #define PLATFORM_MUTEX_LOCK(m) EnterCriticalSection(m) | |
123 | #define PLATFORM_MUTEX_UNLOCK(m) LeaveCriticalSection(m) | |
124 | #define PLATFORM_MUTEX_DESTROY(m) DeleteCriticalSection(m) | |
125 | #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ | |
126 | InterlockedCompareExchangePointer(dest, newval, oldval) | |
127 | ||
128 | ||
129 | #elif defined(POSIX) | |
130 | #define MUTEX_TYPE pthread_mutex_t | |
131 | #define PLATFORM_MUTEX_INIT(m) pthread_mutex_init(m, NULL) | |
132 | #define PLATFORM_MUTEX_LOCK(m) pthread_mutex_lock(m) | |
133 | #define PLATFORM_MUTEX_UNLOCK(m) pthread_mutex_unlock(m) | |
134 | #define PLATFORM_MUTEX_DESTROY(m) pthread_mutex_destroy(m) | |
135 | #define PLATFORM_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER | |
136 | #if (U_HAVE_GCC_ATOMICS == 1) | |
137 | #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ | |
138 | __sync_val_compare_and_swap(dest, oldval, newval) | |
139 | #else | |
140 | #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ | |
141 | mutexed_compare_and_swap(dest, newval, oldval) | |
142 | #endif | |
143 | ||
144 | ||
145 | #else | |
146 | /* Unknown platform. Note that user can still set mutex functions at run time. */ | |
147 | #define MUTEX_TYPE void * | |
148 | #define PLATFORM_MUTEX_INIT(m) | |
149 | #define PLATFORM_MUTEX_LOCK(m) | |
150 | #define PLATFORM_MUTEX_UNLOCK(m) | |
151 | #define PLATFORM_MUTEX_DESTROY(m) | |
152 | #define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \ | |
153 | mutexed_compare_and_swap(dest, newval, oldval) | |
154 | ||
155 | #endif | |
156 | ||
157 | /* Forward declarations */ | |
158 | static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval); | |
159 | typedef struct ICUMutex ICUMutex; | |
160 | ||
161 | /* | |
162 | * ICUMutex One of these is set up for each UMTX that is used by other ICU code. | |
163 | * The opaque UMTX points to the corresponding ICUMutex struct. | |
164 | * | |
165 | * Because the total number of ICU mutexes is quite small, no effort has | |
166 | * been made to squeeze every byte out of this struct. | |
167 | */ | |
168 | struct ICUMutex { | |
169 | UMTX *owner; /* Points back to the UMTX corrsponding to this */ | |
170 | /* ICUMutex object. */ | |
171 | ||
172 | UBool heapAllocated; /* Set if this ICUMutex is heap allocated, and */ | |
173 | /* will need to be deleted. The global mutex */ | |
174 | /* is static on POSIX platforms; all others */ | |
175 | /* will be heap allocated. */ | |
176 | ||
177 | ICUMutex *next; /* All ICUMutexes are chained into a list so that */ | |
178 | /* they can be found and deleted by u_cleanup(). */ | |
179 | ||
180 | int32_t recursionCount; /* For debugging, detect recursive mutex locks. */ | |
181 | ||
182 | MUTEX_TYPE platformMutex; /* The underlying OS mutex being wrapped. */ | |
183 | ||
184 | UMTX userMutex; /* For use with u_setMutexFunctions operations, */ | |
185 | /* corresponds to platformMutex. */ | |
186 | }; | |
187 | ||
188 | ||
189 | /* The global ICU mutex. | |
190 | * For POSIX platforms, it gets a C style initialization, and is ready to use | |
191 | * at program startup. | |
192 | * For Windows, it will be lazily instantiated on first use. | |
193 | */ | |
194 | ||
195 | #if defined(POSIX) | |
196 | static UMTX globalUMTX; | |
197 | static ICUMutex globalMutex = {&globalUMTX, FALSE, NULL, 0, PLATFORM_MUTEX_INITIALIZER, NULL}; | |
198 | static UMTX globalUMTX = &globalMutex; | |
199 | #else | |
200 | static UMTX globalUMTX = NULL; | |
201 | #endif | |
202 | ||
203 | /* Head of the list of all ICU mutexes. | |
204 | * Linked list is through ICUMutex::next | |
205 | * Modifications to the list are synchronized with the global mutex. | |
206 | * The list is used by u_cleanup(), which needs to dispose of all of the ICU mutexes. | |
207 | * | |
208 | * The statically initialized global mutex on POSIX platforms does not get added to this | |
209 | * mutex list, but that's not a problem - the global mutex gets special handling | |
210 | * during u_cleanup(). | |
211 | */ | |
212 | static ICUMutex *mutexListHead; | |
213 | ||
214 | ||
215 | /* | |
216 | * User mutex implementation functions. If non-null, call back to these rather than | |
217 | * directly using the system (Posix or Windows) APIs. See u_setMutexFunctions(). | |
218 | * (declarations are in uclean.h) | |
219 | */ | |
220 | static UMtxInitFn *pMutexInitFn = NULL; | |
221 | static UMtxFn *pMutexDestroyFn = NULL; | |
222 | static UMtxFn *pMutexLockFn = NULL; | |
223 | static UMtxFn *pMutexUnlockFn = NULL; | |
224 | static const void *gMutexContext = NULL; | |
225 | ||
226 | ||
227 | /* | |
228 | * umtx_lock | |
229 | */ | |
230 | U_CAPI void U_EXPORT2 | |
231 | umtx_lock(UMTX *mutex) | |
232 | { | |
233 | ICUMutex *m; | |
234 | ||
235 | if (mutex == NULL) { | |
236 | mutex = &globalUMTX; | |
237 | } | |
238 | m = (ICUMutex *)*mutex; | |
239 | if (m == NULL) { | |
240 | /* See note on lazy initialization, above. We can get away with it here, with mutexes, | |
241 | * where we couldn't with normal user level data. | |
242 | */ | |
243 | umtx_init(mutex); | |
244 | m = (ICUMutex *)*mutex; | |
245 | } | |
246 | U_ASSERT(m->owner == mutex); | |
247 | ||
248 | if (pMutexLockFn != NULL) { | |
249 | (*pMutexLockFn)(gMutexContext, &m->userMutex); | |
250 | } else { | |
251 | PLATFORM_MUTEX_LOCK(&m->platformMutex); | |
252 | } | |
253 | ||
254 | #if defined(U_DEBUG) | |
255 | m->recursionCount++; /* Recursion causes deadlock on Unixes. */ | |
256 | U_ASSERT(m->recursionCount == 1); /* Recursion detection works on Windows. */ | |
257 | /* Assertion failure on non-Windows indicates a */ | |
258 | /* problem with the mutex implementation itself. */ | |
259 | #endif | |
260 | } | |
261 | ||
262 | ||
263 | ||
264 | /* | |
265 | * umtx_unlock | |
266 | */ | |
267 | U_CAPI void U_EXPORT2 | |
268 | umtx_unlock(UMTX* mutex) | |
269 | { | |
270 | ICUMutex *m; | |
271 | if(mutex == NULL) { | |
272 | mutex = &globalUMTX; | |
273 | } | |
274 | m = (ICUMutex *)*mutex; | |
275 | if (m == NULL) { | |
276 | U_ASSERT(FALSE); /* This mutex is not initialized. */ | |
277 | return; | |
278 | } | |
279 | U_ASSERT(m->owner == mutex); | |
280 | ||
281 | #if defined (U_DEBUG) | |
282 | m->recursionCount--; | |
283 | U_ASSERT(m->recursionCount == 0); /* Detect unlock of an already unlocked mutex */ | |
284 | #endif | |
285 | ||
286 | if (pMutexUnlockFn) { | |
287 | (*pMutexUnlockFn)(gMutexContext, &m->userMutex); | |
288 | } else { | |
289 | PLATFORM_MUTEX_UNLOCK(&m->platformMutex); | |
290 | } | |
291 | } | |
292 | ||
293 | ||
294 | /* umtx_ct Allocate and initialize a new ICUMutex. | |
295 | * If a non-null pointer is supplied, initialize an existing ICU Mutex. | |
296 | */ | |
297 | static ICUMutex *umtx_ct(ICUMutex *m) { | |
298 | if (m == NULL) { | |
299 | m = (ICUMutex *)uprv_malloc(sizeof(ICUMutex)); | |
300 | m->heapAllocated = TRUE; | |
301 | } | |
302 | m->next = NULL; /* List of mutexes is maintained at a higher level. */ | |
303 | m->recursionCount = 0; | |
304 | m->userMutex = NULL; | |
305 | if (pMutexInitFn != NULL) { | |
306 | UErrorCode status = U_ZERO_ERROR; | |
307 | (*pMutexInitFn)(gMutexContext, &m->userMutex, &status); | |
308 | U_ASSERT(U_SUCCESS(status)); | |
309 | } else { | |
310 | PLATFORM_MUTEX_INIT(&m->platformMutex); | |
311 | } | |
312 | return m; | |
313 | } | |
314 | ||
315 | ||
316 | /* umtx_dt Delete a ICUMutex. Destroy the underlying OS Platform mutex. | |
317 | * Does not touch the linked list of ICU Mutexes. | |
318 | */ | |
319 | static void umtx_dt(ICUMutex *m) { | |
320 | if (pMutexDestroyFn != NULL) { | |
321 | (*pMutexDestroyFn)(gMutexContext, &m->userMutex); | |
322 | m->userMutex = NULL; | |
323 | } else { | |
324 | PLATFORM_MUTEX_DESTROY(&m->platformMutex); | |
325 | } | |
326 | ||
327 | if (m->heapAllocated) { | |
328 | uprv_free(m); | |
329 | } | |
330 | } | |
331 | ||
332 | ||
333 | U_CAPI void U_EXPORT2 | |
334 | umtx_init(UMTX *mutex) { | |
335 | ICUMutex *m = NULL; | |
336 | void *originalValue; | |
337 | ||
338 | if (*mutex != NULL) { | |
339 | /* Mutex is already initialized. | |
340 | * Multiple umtx_init()s of a UMTX by other ICU code are explicitly permitted. | |
341 | */ | |
342 | return; | |
343 | } | |
344 | #if defined(POSIX) | |
345 | if (mutex == &globalUMTX) { | |
346 | m = &globalMutex; | |
347 | } | |
348 | #endif | |
349 | ||
350 | m = umtx_ct(m); | |
351 | originalValue = SYNC_COMPARE_AND_SWAP(mutex, NULL, m); | |
352 | if (originalValue != NULL) { | |
353 | umtx_dt(m); | |
354 | return; | |
355 | } | |
356 | ||
357 | m->owner = mutex; | |
358 | ||
359 | /* Hook the new mutex into the list of all ICU mutexes, so that we can find and | |
360 | * delete it for u_cleanup(). | |
361 | */ | |
362 | ||
363 | umtx_lock(NULL); | |
364 | m->next = mutexListHead; | |
365 | mutexListHead = m; | |
366 | umtx_unlock(NULL); | |
367 | return; | |
368 | } | |
369 | ||
370 | ||
371 | /* | |
372 | * umtx_destroy. Un-initialize a mutex, releasing any underlying resources | |
373 | * that it may be holding. Destroying an already destroyed | |
374 | * mutex has no effect. Unlike umtx_init(), this function | |
375 | * is not thread safe; two threads must not concurrently try to | |
376 | * destroy the same mutex. | |
377 | */ | |
378 | U_CAPI void U_EXPORT2 | |
379 | umtx_destroy(UMTX *mutex) { | |
380 | ICUMutex *m; | |
381 | ||
382 | /* No one should be deleting the global ICU mutex. | |
383 | * (u_cleanup() does delete it, but does so explicitly, not by passing NULL) | |
384 | */ | |
385 | U_ASSERT(mutex != NULL); | |
386 | if (mutex == NULL) { | |
387 | return; | |
388 | } | |
389 | ||
390 | m = (ICUMutex *)*mutex; | |
391 | if (m == NULL) { /* Mutex not initialized, or already destroyed. */ | |
392 | return; | |
393 | } | |
394 | ||
395 | U_ASSERT(m->owner == mutex); | |
396 | if (m->owner != mutex) { | |
397 | return; | |
398 | } | |
399 | ||
400 | /* Remove this mutex from the linked list of mutexes. */ | |
401 | umtx_lock(NULL); | |
402 | if (mutexListHead == m) { | |
403 | mutexListHead = m->next; | |
404 | } else { | |
405 | ICUMutex *prev; | |
406 | for (prev = mutexListHead; prev!=NULL && prev->next!=m; prev = prev->next); | |
407 | /* Empty for loop body */ | |
408 | if (prev != NULL) { | |
409 | prev->next = m->next; | |
410 | } | |
411 | } | |
412 | umtx_unlock(NULL); | |
413 | ||
414 | umtx_dt(m); /* Delete the internal ICUMutex */ | |
415 | *mutex = NULL; /* Clear the caller's UMTX */ | |
416 | } | |
417 | ||
418 | ||
419 | ||
420 | U_CAPI void U_EXPORT2 | |
421 | u_setMutexFunctions(const void *context, UMtxInitFn *i, UMtxFn *d, UMtxFn *l, UMtxFn *u, | |
422 | UErrorCode *status) { | |
423 | if (U_FAILURE(*status)) { | |
424 | return; | |
425 | } | |
426 | ||
427 | /* Can not set a mutex function to a NULL value */ | |
428 | if (i==NULL || d==NULL || l==NULL || u==NULL) { | |
429 | *status = U_ILLEGAL_ARGUMENT_ERROR; | |
430 | return; | |
431 | } | |
432 | ||
433 | /* If ICU is not in an initial state, disallow this operation. */ | |
434 | if (cmemory_inUse()) { | |
435 | *status = U_INVALID_STATE_ERROR; | |
436 | return; | |
437 | } | |
438 | ||
439 | /* Kill any existing global mutex. POSIX platforms have a global mutex | |
440 | * even before any other part of ICU is initialized. | |
441 | */ | |
442 | umtx_destroy(&globalUMTX); | |
443 | ||
444 | /* Swap in the mutex function pointers. */ | |
445 | pMutexInitFn = i; | |
446 | pMutexDestroyFn = d; | |
447 | pMutexLockFn = l; | |
448 | pMutexUnlockFn = u; | |
449 | gMutexContext = context; | |
450 | ||
451 | #if defined (POSIX) | |
452 | /* POSIX platforms must have a pre-initialized global mutex | |
453 | * to allow other mutexes to initialize safely. */ | |
454 | umtx_init(&globalUMTX); | |
455 | #endif | |
456 | } | |
457 | ||
458 | ||
459 | /* synchronized compare and swap function, for use when OS or compiler built-in | |
460 | * equivalents aren't available. | |
461 | * | |
462 | * This operation relies on the ICU global mutex for synchronization. | |
463 | * | |
464 | * There are two cases where this function can be entered when the global mutex is not | |
465 | * yet initialized - at the end u_cleanup(), and at the end of u_setMutexFunctions, both | |
466 | * of which re-init the global mutex. But neither function is thread-safe, so the lack of | |
467 | * synchronization at these points doesn't matter. | |
468 | */ | |
469 | static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval) { | |
470 | void *temp; | |
471 | UBool needUnlock = FALSE; | |
472 | ||
473 | if (globalUMTX != NULL) { | |
474 | umtx_lock(&globalUMTX); | |
475 | needUnlock = TRUE; | |
476 | } | |
477 | ||
478 | temp = *dest; | |
479 | if (temp == oldval) { | |
480 | *dest = newval; | |
481 | } | |
482 | ||
483 | if (needUnlock) { | |
484 | umtx_unlock(&globalUMTX); | |
485 | } | |
486 | return temp; | |
487 | } | |
488 | ||
489 | ||
490 | ||
491 | /*----------------------------------------------------------------- | |
492 | * | |
493 | * Atomic Increment and Decrement | |
494 | * umtx_atomic_inc | |
495 | * umtx_atomic_dec | |
496 | * | |
497 | *----------------------------------------------------------------*/ | |
498 | ||
499 | /* Pointers to user-supplied inc/dec functions. Null if no funcs have been set. */ | |
500 | static UMtxAtomicFn *pIncFn = NULL; | |
501 | static UMtxAtomicFn *pDecFn = NULL; | |
502 | static const void *gIncDecContext = NULL; | |
503 | ||
504 | static UMTX gIncDecMutex = NULL; | |
505 | ||
506 | U_CAPI int32_t U_EXPORT2 | |
507 | umtx_atomic_inc(int32_t *p) { | |
508 | int32_t retVal; | |
509 | if (pIncFn) { | |
510 | retVal = (*pIncFn)(gIncDecContext, p); | |
511 | } else { | |
512 | #if defined (U_WINDOWS) && ICU_USE_THREADS == 1 | |
513 | retVal = InterlockedIncrement((LONG*)p); | |
514 | #elif defined(USE_MAC_OS_ATOMIC_INCREMENT) | |
515 | retVal = OSAtomicIncrement32Barrier(p); | |
516 | #elif (U_HAVE_GCC_ATOMICS == 1) | |
517 | retVal = __sync_add_and_fetch(p, 1); | |
518 | #elif defined (POSIX) && ICU_USE_THREADS == 1 | |
519 | umtx_lock(&gIncDecMutex); | |
520 | retVal = ++(*p); | |
521 | umtx_unlock(&gIncDecMutex); | |
522 | #else | |
523 | /* Unknown Platform, or ICU thread support compiled out. */ | |
524 | retVal = ++(*p); | |
525 | #endif | |
526 | } | |
527 | return retVal; | |
528 | } | |
529 | ||
530 | U_CAPI int32_t U_EXPORT2 | |
531 | umtx_atomic_dec(int32_t *p) { | |
532 | int32_t retVal; | |
533 | if (pDecFn) { | |
534 | retVal = (*pDecFn)(gIncDecContext, p); | |
535 | } else { | |
536 | #if defined (U_WINDOWS) && ICU_USE_THREADS == 1 | |
537 | retVal = InterlockedDecrement((LONG*)p); | |
538 | #elif defined(USE_MAC_OS_ATOMIC_INCREMENT) | |
539 | retVal = OSAtomicDecrement32Barrier(p); | |
540 | #elif (U_HAVE_GCC_ATOMICS == 1) | |
541 | retVal = __sync_sub_and_fetch(p, 1); | |
542 | #elif defined (POSIX) && ICU_USE_THREADS == 1 | |
543 | umtx_lock(&gIncDecMutex); | |
544 | retVal = --(*p); | |
545 | umtx_unlock(&gIncDecMutex); | |
546 | #else | |
547 | /* Unknown Platform, or ICU thread support compiled out. */ | |
548 | retVal = --(*p); | |
549 | #endif | |
550 | } | |
551 | return retVal; | |
552 | } | |
553 | ||
554 | ||
555 | ||
556 | U_CAPI void U_EXPORT2 | |
557 | u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *ip, UMtxAtomicFn *dp, | |
558 | UErrorCode *status) { | |
559 | if (U_FAILURE(*status)) { | |
560 | return; | |
561 | } | |
562 | /* Can not set a mutex function to a NULL value */ | |
563 | if (ip==NULL || dp==NULL) { | |
564 | *status = U_ILLEGAL_ARGUMENT_ERROR; | |
565 | return; | |
566 | } | |
567 | /* If ICU is not in an initial state, disallow this operation. */ | |
568 | if (cmemory_inUse()) { | |
569 | *status = U_INVALID_STATE_ERROR; | |
570 | return; | |
571 | } | |
572 | ||
573 | pIncFn = ip; | |
574 | pDecFn = dp; | |
575 | gIncDecContext = context; | |
576 | ||
577 | #if !U_RELEASE | |
578 | { | |
579 | int32_t testInt = 0; | |
580 | U_ASSERT(umtx_atomic_inc(&testInt) == 1); /* Sanity Check. Do the functions work at all? */ | |
581 | U_ASSERT(testInt == 1); | |
582 | U_ASSERT(umtx_atomic_dec(&testInt) == 0); | |
583 | U_ASSERT(testInt == 0); | |
584 | } | |
585 | #endif | |
586 | } | |
587 | ||
588 | ||
589 | ||
590 | /* | |
591 | * Mutex Cleanup Function | |
592 | * | |
593 | * Destroy the global mutex(es), and reset the mutex function callback pointers. | |
594 | */ | |
595 | U_CFUNC UBool umtx_cleanup(void) { | |
596 | ICUMutex *thisMutex = NULL; | |
597 | ICUMutex *nextMutex = NULL; | |
598 | ||
599 | /* Extra, do-nothing function call to suppress compiler warnings on platforms where | |
600 | * mutexed_compare_and_swap is not otherwise used. */ | |
601 | mutexed_compare_and_swap(&globalUMTX, NULL, NULL); | |
602 | ||
603 | /* Delete all of the ICU mutexes. Do the global mutex last because it is used during | |
604 | * the umtx_destroy operation of other mutexes. | |
605 | */ | |
606 | for (thisMutex=mutexListHead; thisMutex!=NULL; thisMutex=nextMutex) { | |
607 | UMTX *umtx = thisMutex->owner; | |
608 | nextMutex = thisMutex->next; | |
609 | U_ASSERT(*umtx = (void *)thisMutex); | |
610 | if (umtx != &globalUMTX) { | |
611 | umtx_destroy(umtx); | |
612 | } | |
613 | } | |
614 | umtx_destroy(&globalUMTX); | |
615 | ||
616 | pMutexInitFn = NULL; | |
617 | pMutexDestroyFn = NULL; | |
618 | pMutexLockFn = NULL; | |
619 | pMutexUnlockFn = NULL; | |
620 | gMutexContext = NULL; | |
621 | pIncFn = NULL; | |
622 | pDecFn = NULL; | |
623 | gIncDecContext = NULL; | |
624 | gIncDecMutex = NULL; | |
625 | ||
626 | #if defined (POSIX) | |
627 | /* POSIX platforms must come out of u_cleanup() with a functioning global mutex | |
628 | * to permit the safe resumption of use of ICU in multi-threaded environments. | |
629 | */ | |
630 | umtx_init(&globalUMTX); | |
631 | #endif | |
632 | return TRUE; | |
633 | } | |
634 | ||
635 |