]>
Commit | Line | Data |
---|---|---|
b75a7d8f A |
1 | /* |
2 | ****************************************************************************** | |
3 | * | |
374ca955 | 4 | * Copyright (C) 1997-2004, International Business Machines |
b75a7d8f A |
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 | /* Assume POSIX, and modify as necessary below */ | |
22 | #define POSIX | |
23 | ||
24 | #if defined(_WIN32) | |
25 | #undef POSIX | |
26 | #endif | |
27 | #if defined(macintosh) | |
28 | #undef POSIX | |
29 | #endif | |
30 | #if defined(OS2) | |
31 | #undef POSIX | |
32 | #endif | |
33 | ||
34 | ||
b75a7d8f A |
35 | #include "unicode/utypes.h" |
36 | #include "uassert.h" | |
374ca955 | 37 | #include "ucln_cmn.h" |
b75a7d8f A |
38 | |
39 | ||
40 | #if defined(POSIX) && (ICU_USE_THREADS==1) | |
b75a7d8f A |
41 | # include <pthread.h> /* must be first, so that we get the multithread versions of things. */ |
42 | ||
b75a7d8f A |
43 | #endif /* POSIX && (ICU_USE_THREADS==1) */ |
44 | ||
45 | #ifdef WIN32 | |
46 | # define WIN32_LEAN_AND_MEAN | |
374ca955 | 47 | # define VC_EXTRALEAN |
b75a7d8f A |
48 | # define NOUSER |
49 | # define NOSERVICE | |
50 | # define NOIME | |
51 | # define NOMCX | |
52 | # include <windows.h> | |
53 | #endif | |
54 | ||
55 | #include "umutex.h" | |
56 | #include "cmemory.h" | |
57 | ||
374ca955 A |
58 | /* |
59 | * A note on ICU Mutex Initialization and ICU startup: | |
60 | * | |
61 | * ICU mutexes, as used through the rest of the ICU code, are self-initializing. | |
62 | * To make this work, ICU uses the _ICU GLobal Mutex_ to synchronize the lazy init | |
63 | * of other ICU mutexes. For the global mutex itself, we need some other mechanism | |
64 | * to safely initialize it on first use. This becomes important if two or more | |
65 | * threads were more or less simultaenously the first to use ICU in a process, and | |
66 | * were racing into the mutex initialization code. | |
67 | * | |
68 | * The solution for the global mutex init is platform dependent. | |
69 | * On POSIX systems, C-style init can be used on a mutex, with the | |
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 | |
77 | * avoid problems with race conditions. | |
78 | * | |
79 | * If an application has overridden the ICU mutex implementation | |
80 | * by calling u_setMutexFunctions(), the user supplied init function must | |
81 | * be safe in the event that multiple threads concurrently attempt to init | |
82 | * the same mutex. The first thread should do the init, and the others should | |
83 | * have no effect. | |
84 | * | |
85 | */ | |
86 | ||
87 | #define MAX_MUTEXES 30 | |
88 | static UMTX gGlobalMutex = NULL; | |
89 | static UMTX gIncDecMutex = NULL; | |
b75a7d8f | 90 | #if (ICU_USE_THREADS == 1) |
374ca955 A |
91 | static UBool gMutexPoolInitialized = FALSE; |
92 | static char gMutexesInUse[MAX_MUTEXES]; | |
93 | ||
94 | #if defined(WIN32) | |
95 | /*------------------------------------------------------------- | |
96 | * | |
97 | * WINDOWS platform variable declarations | |
98 | * | |
99 | *-------------------------------------------------------------*/ | |
100 | static CRITICAL_SECTION gMutexes[MAX_MUTEXES]; | |
101 | static CRITICAL_SECTION gGlobalWinMutex; | |
102 | ||
103 | ||
104 | /* On WIN32 mutexes are reentrant. This makes it difficult to debug | |
105 | * deadlocking problems that show up on POSIXy platforms, where | |
106 | * mutexes deadlock upon reentry. ICU contains checking code for | |
107 | * the global mutex as well as for other mutexes in the pool. | |
108 | * | |
109 | * This is for debugging purposes. | |
110 | * | |
111 | * This has no effect on non-WIN32 platforms, non-DEBUG builds, and | |
112 | * non-ICU_USE_THREADS builds. | |
113 | * | |
114 | * Note: The CRITICAL_SECTION structure already has a RecursionCount | |
115 | * member that can be used for this purpose, but portability to | |
116 | * Win98/NT/2K needs to be tested before use. Works fine on XP. | |
117 | * After portability is confirmed, the built-in RecursionCount can be | |
118 | * used, and the gRecursionCountPool can be removed. | |
119 | * | |
120 | * Note: Non-global mutex checking only happens if there is no custom | |
121 | * pMutexLockFn defined. Use one function, not two (don't use | |
122 | * pMutexLockFn and pMutexUnlockFn) so the increment and decrement of | |
123 | * the recursion count don't get out of sync. Users might set just | |
124 | * one function, e.g., to perform a custom action, followed by a | |
125 | * standard call to EnterCriticalSection. | |
126 | */ | |
127 | #if defined(U_DEBUG) && (ICU_USE_THREADS==1) | |
128 | static int32_t gRecursionCount = 0; /* detect global mutex locking */ | |
129 | static int32_t gRecursionCountPool[MAX_MUTEXES]; /* ditto for non-global */ | |
130 | #endif | |
b75a7d8f | 131 | |
b75a7d8f | 132 | |
374ca955 A |
133 | #elif defined(POSIX) |
134 | /*------------------------------------------------------------- | |
135 | * | |
136 | * POSIX platform variable declarations | |
137 | * | |
138 | *-------------------------------------------------------------*/ | |
139 | static pthread_mutex_t gMutexes[MAX_MUTEXES] = { | |
140 | PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, | |
141 | PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, | |
142 | PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, | |
143 | PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, | |
144 | PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, | |
145 | PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, | |
146 | PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER | |
147 | }; | |
b75a7d8f | 148 | |
374ca955 A |
149 | #else |
150 | /*------------------------------------------------------------- | |
151 | * | |
152 | * UNKNOWN platform declarations | |
153 | * | |
154 | *-------------------------------------------------------------*/ | |
155 | static void *gMutexes[MAX_MUTEXES] = { | |
156 | NULL, NULL, NULL, | |
157 | NULL, NULL, NULL, | |
158 | NULL, NULL, NULL, | |
159 | NULL, NULL, NULL, | |
160 | NULL, NULL, NULL, | |
161 | NULL, NULL, NULL, | |
162 | NULL, NULL }; | |
163 | ||
164 | /* Unknown platform. OK so long as ICU_USE_THREAD is not set. | |
165 | Note that user can still set mutex functions at run time, | |
166 | and that the global mutex variable is still needed in that case. */ | |
167 | #if (ICU_USE_THREADS == 1) | |
168 | #error no ICU mutex implementation for this platform | |
169 | #endif | |
b75a7d8f A |
170 | #endif |
171 | #endif /* ICU_USE_THREADS==1 */ | |
172 | ||
173 | ||
174 | ||
b75a7d8f | 175 | |
374ca955 A |
176 | /* |
177 | * User mutex implementation functions. If non-null, call back to these rather than | |
178 | * directly using the system (Posix or Windows) APIs. | |
179 | * (declarations are in uclean.h) | |
180 | */ | |
181 | static UMtxInitFn *pMutexInitFn = NULL; | |
182 | static UMtxFn *pMutexDestroyFn = NULL; | |
183 | static UMtxFn *pMutexLockFn = NULL; | |
184 | static UMtxFn *pMutexUnlockFn = NULL; | |
185 | static const void *gMutexContext = NULL; | |
186 | ||
187 | ||
188 | ||
189 | /* | |
190 | * umtx_lock | |
191 | */ | |
b75a7d8f A |
192 | U_CAPI void U_EXPORT2 |
193 | umtx_lock(UMTX *mutex) | |
194 | { | |
374ca955 | 195 | if (mutex == NULL) { |
b75a7d8f A |
196 | mutex = &gGlobalMutex; |
197 | } | |
198 | ||
374ca955 A |
199 | if (*mutex == NULL) { |
200 | /* Lock of an uninitialized mutex. Initialize it before proceeding. */ | |
201 | umtx_init(mutex); | |
b75a7d8f A |
202 | } |
203 | ||
374ca955 A |
204 | if (pMutexLockFn != NULL) { |
205 | (*pMutexLockFn)(gMutexContext, mutex); | |
206 | } else { | |
b75a7d8f | 207 | |
374ca955 A |
208 | #if (ICU_USE_THREADS == 1) |
209 | #if defined(WIN32) | |
210 | EnterCriticalSection((CRITICAL_SECTION*) *mutex); | |
b75a7d8f | 211 | #elif defined(POSIX) |
374ca955 A |
212 | pthread_mutex_lock((pthread_mutex_t*) *mutex); |
213 | #endif /* cascade of platforms */ | |
214 | #endif /* ICU_USE_THREADS==1 */ | |
215 | } | |
b75a7d8f | 216 | |
374ca955 A |
217 | #if defined(WIN32) && defined(U_DEBUG) && (ICU_USE_THREADS==1) |
218 | if (mutex == &gGlobalMutex) { /* Detect Reentrant locking of the global mutex. */ | |
219 | gRecursionCount++; /* Recursion causes deadlocks on Unixes. */ | |
220 | U_ASSERT(gRecursionCount == 1); /* Detection works on Windows. Debug problems there. */ | |
b75a7d8f | 221 | } |
374ca955 A |
222 | /* This handles gGlobalMutex too, but only if there is no pMutexLockFn */ |
223 | else if (pMutexLockFn == NULL) { /* see comments above */ | |
224 | int i = ((CRITICAL_SECTION*)*mutex) - &gMutexes[0]; | |
225 | U_ASSERT(i >= 0 && i < MAX_MUTEXES); | |
226 | ++gRecursionCountPool[i]; | |
227 | ||
228 | U_ASSERT(gRecursionCountPool[i] == 1); /* !Detect Deadlock! */ | |
229 | ||
230 | /* This works and is fast, but needs testing on Win98/NT/2K. | |
231 | See comments above. [alan] | |
232 | U_ASSERT((CRITICAL_SECTION*)*mutex >= &gMutexes[0] && | |
233 | (CRITICAL_SECTION*)*mutex <= &gMutexes[MAX_MUTEXES]); | |
234 | U_ASSERT(((CRITICAL_SECTION*)*mutex)->RecursionCount == 1); | |
235 | */ | |
236 | } | |
237 | #endif /*U_DEBUG*/ | |
b75a7d8f A |
238 | } |
239 | ||
374ca955 A |
240 | |
241 | ||
242 | /* | |
243 | * umtx_unlock | |
244 | */ | |
b75a7d8f A |
245 | U_CAPI void U_EXPORT2 |
246 | umtx_unlock(UMTX* mutex) | |
247 | { | |
374ca955 | 248 | if(mutex == NULL) { |
b75a7d8f A |
249 | mutex = &gGlobalMutex; |
250 | } | |
251 | ||
374ca955 A |
252 | if(*mutex == NULL) { |
253 | #if (ICU_USE_THREADS == 1) | |
254 | U_ASSERT(FALSE); /* This mutex is not initialized. */ | |
255 | #endif | |
256 | return; | |
b75a7d8f A |
257 | } |
258 | ||
374ca955 | 259 | #if defined (WIN32) && defined (U_DEBUG) && (ICU_USE_THREADS==1) |
b75a7d8f A |
260 | if (mutex == &gGlobalMutex) { |
261 | gRecursionCount--; | |
374ca955 | 262 | U_ASSERT(gRecursionCount == 0); /* Detect unlock of an already unlocked mutex */ |
b75a7d8f | 263 | } |
374ca955 A |
264 | /* This handles gGlobalMutex too, but only if there is no pMutexLockFn */ |
265 | else if (pMutexLockFn == NULL) { /* see comments above */ | |
266 | int i = ((CRITICAL_SECTION*)*mutex) - &gMutexes[0]; | |
267 | U_ASSERT(i >= 0 && i < MAX_MUTEXES); | |
268 | --gRecursionCountPool[i]; | |
269 | ||
270 | U_ASSERT(gRecursionCountPool[i] == 0); /* !Detect Deadlock! */ | |
271 | ||
272 | /* This works and is fast, but needs testing on Win98/NT/2K. | |
273 | Note that RecursionCount will be 1, not 0, since we haven't | |
274 | left the CRITICAL_SECTION yet. See comments above. [alan] | |
275 | U_ASSERT((CRITICAL_SECTION*)*mutex >= &gMutexes[0] && | |
276 | (CRITICAL_SECTION*)*mutex <= &gMutexes[MAX_MUTEXES]); | |
277 | U_ASSERT(((CRITICAL_SECTION*)*mutex)->RecursionCount == 1); | |
278 | */ | |
b75a7d8f A |
279 | } |
280 | #endif | |
281 | ||
374ca955 A |
282 | if (pMutexUnlockFn) { |
283 | (*pMutexUnlockFn)(gMutexContext, mutex); | |
284 | } else { | |
285 | #if (ICU_USE_THREADS==1) | |
286 | #if defined (WIN32) | |
287 | LeaveCriticalSection((CRITICAL_SECTION*)*mutex); | |
288 | #elif defined (POSIX) | |
289 | pthread_mutex_unlock((pthread_mutex_t*)*mutex); | |
290 | #endif /* cascade of platforms */ | |
b75a7d8f | 291 | #endif /* ICU_USE_THREADS == 1 */ |
374ca955 | 292 | } |
b75a7d8f A |
293 | } |
294 | ||
295 | ||
296 | ||
374ca955 | 297 | |
b75a7d8f | 298 | /* |
374ca955 A |
299 | * initGlobalMutex Do the platform specific initialization of the ICU global mutex. |
300 | * Separated out from the other mutexes because it is different: | |
301 | * Mutex storage is static for POSIX, init must be thread safe | |
302 | * without the use of another mutex. | |
b75a7d8f | 303 | */ |
374ca955 A |
304 | static void initGlobalMutex() { |
305 | /* | |
306 | * If User Supplied mutex functions are in use | |
307 | * init the icu global mutex using them. | |
308 | */ | |
309 | if (pMutexInitFn != NULL) { | |
310 | if (gGlobalMutex==NULL) { | |
311 | UErrorCode status = U_ZERO_ERROR; | |
312 | (*pMutexInitFn)(gMutexContext, &gGlobalMutex, &status); | |
313 | if (U_FAILURE(status)) { | |
314 | /* TODO: how should errors here be handled? */ | |
315 | return; | |
316 | } | |
317 | } | |
318 | return; | |
319 | } | |
320 | ||
321 | /* No user override of mutex functions. | |
322 | * Use default ICU mutex implementations. | |
323 | */ | |
b75a7d8f | 324 | #if (ICU_USE_THREADS == 1) |
374ca955 A |
325 | /* |
326 | * for Windows, init the pool of critical sections that we | |
327 | * will use as needed for ICU mutexes. | |
328 | */ | |
329 | #if defined (WIN32) | |
330 | if (gMutexPoolInitialized == FALSE) { | |
331 | int i; | |
332 | for (i=0; i<MAX_MUTEXES; i++) { | |
333 | InitializeCriticalSection(&gMutexes[i]); | |
334 | #if defined (U_DEBUG) | |
335 | gRecursionCountPool[i] = 0; /* see comments above */ | |
336 | #endif | |
b75a7d8f | 337 | } |
374ca955 A |
338 | gMutexPoolInitialized = TRUE; |
339 | } | |
340 | #elif defined (POSIX) | |
341 | /* TODO: experimental code. Shouldn't need to explicitly init the mutexes. */ | |
342 | if (gMutexPoolInitialized == FALSE) { | |
343 | int i; | |
344 | for (i=0; i<MAX_MUTEXES; i++) { | |
345 | pthread_mutex_init(&gMutexes[i], NULL); | |
b75a7d8f | 346 | } |
374ca955 A |
347 | gMutexPoolInitialized = TRUE; |
348 | } | |
349 | #endif | |
350 | ||
351 | /* | |
352 | * for both Windows & POSIX, the first mutex in the array is used | |
353 | * for the ICU global mutex. | |
354 | */ | |
355 | gGlobalMutex = &gMutexes[0]; | |
356 | gMutexesInUse[0] = 1; | |
357 | ||
358 | #else /* ICU_USE_THREADS */ | |
359 | gGlobalMutex = &gGlobalMutex; /* With no threads, we must still set the mutex to | |
360 | * some non-null value to make the rest of the | |
361 | * (not ifdefed) mutex code think that it is initialized. | |
362 | */ | |
363 | #endif /* ICU_USE_THREADS */ | |
b75a7d8f | 364 | } |
b75a7d8f A |
365 | |
366 | ||
b75a7d8f | 367 | |
b75a7d8f | 368 | |
b75a7d8f | 369 | |
374ca955 A |
370 | U_CAPI void U_EXPORT2 |
371 | umtx_init(UMTX *mutex) | |
372 | { | |
373 | if (mutex == NULL || mutex == &gGlobalMutex) { | |
374 | initGlobalMutex(); | |
b75a7d8f | 375 | } else { |
b75a7d8f | 376 | umtx_lock(NULL); |
374ca955 A |
377 | if (*mutex != NULL) { |
378 | /* Another thread initialized this mutex first. */ | |
379 | umtx_unlock(NULL); | |
b75a7d8f A |
380 | return; |
381 | } | |
382 | ||
374ca955 A |
383 | if (pMutexInitFn != NULL) { |
384 | UErrorCode status = U_ZERO_ERROR; | |
385 | (*pMutexInitFn)(gMutexContext, mutex, &status); | |
386 | /* TODO: how to report failure on init? */ | |
387 | umtx_unlock(NULL); | |
388 | return; | |
389 | } | |
390 | else { | |
391 | #if (ICU_USE_THREADS == 1) | |
392 | /* Search through our pool of pre-allocated mutexes for one that is not | |
393 | * already in use. */ | |
394 | int i; | |
395 | for (i=0; i<MAX_MUTEXES; i++) { | |
396 | if (gMutexesInUse[i] == 0) { | |
397 | gMutexesInUse[i] = 1; | |
398 | *mutex = &gMutexes[i]; | |
399 | break; | |
400 | } | |
401 | } | |
402 | #endif | |
b75a7d8f A |
403 | } |
404 | umtx_unlock(NULL); | |
374ca955 A |
405 | |
406 | #if (ICU_USE_THREADS == 1) | |
407 | /* No more mutexes were available from our pre-allocated pool. */ | |
408 | /* TODO: how best to deal with this? */ | |
409 | U_ASSERT(*mutex != NULL); | |
410 | #endif | |
b75a7d8f | 411 | } |
b75a7d8f A |
412 | } |
413 | ||
374ca955 A |
414 | |
415 | /* | |
416 | * umtx_destroy. Un-initialize a mutex, releasing any underlying resources | |
417 | * that it may be holding. Destroying an already destroyed | |
418 | * mutex has no effect. Unlike umtx_init(), this function | |
419 | * is not thread safe; two threads must not concurrently try to | |
420 | * destroy the same mutex. | |
421 | */ | |
b75a7d8f A |
422 | U_CAPI void U_EXPORT2 |
423 | umtx_destroy(UMTX *mutex) { | |
374ca955 | 424 | if (mutex == NULL) { /* destroy the global mutex */ |
b75a7d8f A |
425 | mutex = &gGlobalMutex; |
426 | } | |
374ca955 A |
427 | |
428 | if (*mutex == NULL) { /* someone already did it. */ | |
b75a7d8f | 429 | return; |
374ca955 | 430 | } |
b75a7d8f | 431 | |
374ca955 A |
432 | /* The life of the inc/dec mutex is tied to that of the global mutex. */ |
433 | if (mutex == &gGlobalMutex) { | |
434 | umtx_destroy(&gIncDecMutex); | |
435 | } | |
b75a7d8f | 436 | |
374ca955 A |
437 | if (pMutexDestroyFn != NULL) { |
438 | /* Mutexes are being managed by the app. Call back to it for the destroy. */ | |
439 | (*pMutexDestroyFn)(gMutexContext, mutex); | |
440 | } | |
441 | else { | |
442 | #if (ICU_USE_THREADS == 1) | |
443 | /* Return this mutex to the pool of available mutexes, if it came from the | |
444 | * pool in the first place. | |
445 | */ | |
446 | /* TODO use pointer math here, instead of iterating! */ | |
447 | int i; | |
448 | for (i=0; i<MAX_MUTEXES; i++) { | |
449 | if (*mutex == &gMutexes[i]) { | |
450 | gMutexesInUse[i] = 0; | |
451 | break; | |
452 | } | |
453 | } | |
b75a7d8f | 454 | #endif |
b75a7d8f A |
455 | } |
456 | ||
457 | *mutex = NULL; | |
b75a7d8f A |
458 | } |
459 | ||
460 | ||
b75a7d8f | 461 | |
374ca955 A |
462 | U_CAPI void U_EXPORT2 |
463 | u_setMutexFunctions(const void *context, UMtxInitFn *i, UMtxFn *d, UMtxFn *l, UMtxFn *u, | |
464 | UErrorCode *status) { | |
465 | if (U_FAILURE(*status)) { | |
466 | return; | |
467 | } | |
b75a7d8f | 468 | |
374ca955 A |
469 | /* Can not set a mutex function to a NULL value */ |
470 | if (i==NULL || d==NULL || l==NULL || u==NULL) { | |
471 | *status = U_ILLEGAL_ARGUMENT_ERROR; | |
472 | return; | |
473 | } | |
b75a7d8f | 474 | |
374ca955 A |
475 | /* If ICU is not in an initial state, disallow this operation. */ |
476 | if (cmemory_inUse()) { | |
477 | *status = U_INVALID_STATE_ERROR; | |
478 | return; | |
479 | } | |
b75a7d8f | 480 | |
374ca955 A |
481 | /* Swap in the mutex function pointers. */ |
482 | pMutexInitFn = i; | |
483 | pMutexDestroyFn = d; | |
484 | pMutexLockFn = l; | |
485 | pMutexUnlockFn = u; | |
486 | gMutexContext = context; | |
487 | gGlobalMutex = NULL; /* For POSIX, the global mutex will be pre-initialized */ | |
488 | /* Undo that, force re-initialization when u_init() */ | |
489 | /* happens. */ | |
b75a7d8f A |
490 | } |
491 | ||
b75a7d8f | 492 | |
b75a7d8f | 493 | |
374ca955 A |
494 | /*----------------------------------------------------------------- |
495 | * | |
496 | * Atomic Increment and Decrement | |
497 | * umtx_atomic_inc | |
498 | * umtx_atomic_dec | |
499 | * | |
500 | *----------------------------------------------------------------*/ | |
501 | ||
502 | /* Pointers to user-supplied inc/dec functions. Null if no funcs have been set. */ | |
503 | static UMtxAtomicFn *pIncFn = NULL; | |
504 | static UMtxAtomicFn *pDecFn = NULL; | |
505 | static void *gIncDecContext = NULL; | |
b75a7d8f A |
506 | |
507 | ||
508 | U_CAPI int32_t U_EXPORT2 | |
374ca955 A |
509 | umtx_atomic_inc(int32_t *p) { |
510 | int32_t retVal; | |
511 | if (pIncFn) { | |
512 | retVal = (*pIncFn)(gIncDecContext, p); | |
513 | } else { | |
514 | #if defined (WIN32) && ICU_USE_THREADS == 1 | |
515 | retVal = InterlockedIncrement((LONG*)p); | |
516 | #elif defined (POSIX) && ICU_USE_THREADS == 1 | |
517 | umtx_lock(&gIncDecMutex); | |
518 | retVal = ++(*p); | |
519 | umtx_unlock(&gIncDecMutex); | |
520 | #else | |
521 | /* Unknown Platform, or ICU thread support compiled out. */ | |
522 | retVal = ++(*p); | |
523 | #endif | |
524 | } | |
525 | return retVal; | |
526 | } | |
b75a7d8f | 527 | |
374ca955 A |
528 | U_CAPI int32_t U_EXPORT2 |
529 | umtx_atomic_dec(int32_t *p) { | |
530 | int32_t retVal; | |
531 | if (pDecFn) { | |
532 | retVal = (*pDecFn)(gIncDecContext, p); | |
533 | } else { | |
534 | #if defined (WIN32) && ICU_USE_THREADS == 1 | |
535 | retVal = InterlockedDecrement((LONG*)p); | |
536 | #elif defined (POSIX) && ICU_USE_THREADS == 1 | |
537 | umtx_lock(&gIncDecMutex); | |
538 | retVal = --(*p); | |
539 | umtx_unlock(&gIncDecMutex); | |
540 | #else | |
541 | /* Unknown Platform, or ICU thread support compiled out. */ | |
542 | retVal = --(*p); | |
543 | #endif | |
544 | } | |
b75a7d8f A |
545 | return retVal; |
546 | } | |
547 | ||
374ca955 | 548 | /* TODO: Some POSIXy platforms have atomic inc/dec functions available. Use them. */ |
b75a7d8f | 549 | |
b75a7d8f | 550 | |
b75a7d8f A |
551 | |
552 | ||
b75a7d8f | 553 | |
374ca955 A |
554 | U_CAPI void U_EXPORT2 |
555 | u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *ip, UMtxAtomicFn *dp, | |
556 | UErrorCode *status) { | |
557 | int32_t testInt; | |
558 | if (U_FAILURE(*status)) { | |
559 | return; | |
560 | } | |
561 | /* Can not set a mutex function to a NULL value */ | |
562 | if (ip==NULL || dp==NULL) { | |
563 | *status = U_ILLEGAL_ARGUMENT_ERROR; | |
564 | return; | |
565 | } | |
566 | /* If ICU is not in an initial state, disallow this operation. */ | |
567 | if (cmemory_inUse()) { | |
568 | *status = U_INVALID_STATE_ERROR; | |
569 | return; | |
570 | } | |
571 | ||
572 | pIncFn = ip; | |
573 | pDecFn = dp; | |
b75a7d8f | 574 | |
374ca955 A |
575 | testInt = 0; |
576 | U_ASSERT(umtx_atomic_inc(&testInt) == 1); /* Sanity Check. Do the functions work at all? */ | |
577 | U_ASSERT(testInt == 1); | |
578 | U_ASSERT(umtx_atomic_dec(&testInt) == 0); | |
579 | U_ASSERT(testInt == 0); | |
b75a7d8f A |
580 | } |
581 | ||
b75a7d8f | 582 | |
b75a7d8f | 583 | |
374ca955 A |
584 | /* |
585 | * Mutex Cleanup Function | |
586 | * | |
587 | * Destroy the global mutex(es), and reset the mutex function callback pointers. | |
588 | */ | |
589 | U_CFUNC UBool umtx_cleanup(void) { | |
590 | umtx_destroy(NULL); | |
591 | pMutexInitFn = NULL; | |
592 | pMutexDestroyFn = NULL; | |
593 | pMutexLockFn = NULL; | |
594 | pMutexUnlockFn = NULL; | |
595 | gMutexContext = NULL; | |
596 | gGlobalMutex = NULL; | |
597 | pIncFn = NULL; | |
598 | pDecFn = NULL; | |
599 | gIncDecMutex = NULL; | |
600 | ||
601 | #if (ICU_USE_THREADS == 1) | |
602 | if (gMutexPoolInitialized) { | |
603 | int i; | |
604 | for (i=0; i<MAX_MUTEXES; i++) { | |
605 | if (gMutexesInUse[i]) { | |
606 | #if defined (WIN32) | |
607 | DeleteCriticalSection(&gMutexes[i]); | |
608 | #elif defined (POSIX) | |
609 | pthread_mutex_destroy(&gMutexes[i]); | |
610 | #endif | |
611 | gMutexesInUse[i] = 0; | |
612 | } | |
613 | } | |
614 | } | |
615 | gMutexPoolInitialized = FALSE; | |
616 | #endif | |
b75a7d8f | 617 | |
374ca955 A |
618 | return TRUE; |
619 | } | |
b75a7d8f A |
620 | |
621 |