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