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