]> git.saurik.com Git - apple/icu.git/blob - icuSources/common/umutex.c
ICU-3.13.tar.gz
[apple/icu.git] / icuSources / common / umutex.c
1 /*
2 ******************************************************************************
3 *
4 * Copyright (C) 1997-2003, International Business Machines
5 * Corporation and others. All Rights Reserved.
6 *
7 ******************************************************************************
8 *
9 * File CMUTEX.C
10 *
11 * Modification History:
12 *
13 * Date Name Description
14 * 04/02/97 aliu Creation.
15 * 04/07/99 srl updated
16 * 05/13/99 stephen Changed to umutex (from cmutex).
17 * 11/22/99 aliu Make non-global mutex autoinitialize [j151]
18 ******************************************************************************
19 */
20
21 /* 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
35 /* Check our settings... */
36 #include "unicode/utypes.h"
37 #include "uassert.h"
38
39
40 #if defined(POSIX) && (ICU_USE_THREADS==1)
41 /* Usage: uncomment the following, and breakpoint WeAreDeadlocked to
42 find reentrant issues. */
43 /* # define POSIX_DEBUG_REENTRANCY 1 */
44 # include <pthread.h> /* must be first, so that we get the multithread versions of things. */
45
46 # ifdef POSIX_DEBUG_REENTRANCY
47 pthread_t gLastThread;
48 UBool gInMutex;
49
50 U_EXPORT void WeAreDeadlocked();
51
52 void WeAreDeadlocked()
53 {
54 puts("ARGH!! We're deadlocked.. break on WeAreDeadlocked() next time.");
55 }
56 # endif /* POSIX_DEBUG_REENTRANCY */
57 #endif /* POSIX && (ICU_USE_THREADS==1) */
58
59 #ifdef WIN32
60 # define WIN32_LEAN_AND_MEAN
61 # define NOGDI
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
72 #if (ICU_USE_THREADS == 1)
73
74 /* the global mutex. Use it proudly and wash it often. */
75 static UMTX gGlobalMutex = NULL;
76 # ifdef _DEBUG
77 static int32_t gRecursionCount = 0; /* Detect Recursive entries. For debugging only. */
78 # endif
79
80 #if defined(WIN32)
81 static CRITICAL_SECTION gPlatformMutex;
82
83 #elif defined(POSIX)
84 static pthread_mutex_t gPlatformMutex; /* The global ICU mutex */
85 static pthread_mutex_t gIncDecMutex; /* For use by atomic inc/dec, on Unixes only */
86
87 #endif
88 #endif /* ICU_USE_THREADS==1 */
89
90
91
92 U_CAPI UBool U_EXPORT2
93 umtx_isInitialized(UMTX *mutex)
94 {
95 #if (ICU_USE_THREADS == 1)
96 if (mutex == NULL)
97 {
98 return (UBool)(gGlobalMutex != NULL);
99 } else {
100 UBool isInited;
101 umtx_lock(NULL);
102 isInited = (*mutex != NULL);
103 umtx_unlock(NULL);
104 return isInited;
105 }
106 #else
107 return TRUE; /* Since we don't use threads, it's considered initialized. */
108 #endif /* ICU_USE_THREADS==1 */
109 }
110
111 U_CAPI void U_EXPORT2
112 umtx_lock(UMTX *mutex)
113 {
114 #if (ICU_USE_THREADS == 1)
115 if (mutex == NULL)
116 {
117 mutex = &gGlobalMutex;
118 }
119
120 if (*mutex == NULL)
121 {
122 /* Lazy init of a non-global mutexes on first lock is NOT safe on processors
123 * that reorder memory operations. */
124 /* U_ASSERT(FALSE); TODO: Turn this back on */
125 if (mutex != &gGlobalMutex) {
126 umtx_init(mutex);
127 } else {
128 umtx_init(NULL); /* initialize the global mutex - only get
129 here if C++ static init is NOT working,
130 and u_init() hasn't been called.
131
132 Not thread-safe if this call is contended! */
133 }
134 }
135
136 #if defined(WIN32)
137
138 EnterCriticalSection((CRITICAL_SECTION*) *mutex);
139 #ifdef _DEBUG
140 if (mutex == &gGlobalMutex) {
141 gRecursionCount++;
142 U_ASSERT(gRecursionCount == 1);
143 }
144 #endif /*_DEBUG*/
145
146 #elif defined(POSIX)
147
148 # ifdef POSIX_DEBUG_REENTRANCY
149 if (gInMutex == TRUE && mutex == &gGlobalMutex) /* in the mutex -- possible deadlock*/
150 if(pthread_equal(gLastThread, pthread_self()))
151 WeAreDeadlocked();
152 # endif
153 pthread_mutex_lock((pthread_mutex_t*) *mutex);
154
155 # ifdef POSIX_DEBUG_REENTRANCY
156 if (mutex == &gGlobalMutex) {
157 gLastThread = pthread_self();
158 gInMutex = TRUE;
159 }
160 # endif
161 #endif
162 #endif /* ICU_USE_THREADS==1 */
163 }
164
165 U_CAPI void U_EXPORT2
166 umtx_unlock(UMTX* mutex)
167 {
168 #if (ICU_USE_THREADS==1)
169 if(mutex == NULL)
170 {
171 mutex = &gGlobalMutex;
172 }
173
174 if(*mutex == NULL)
175 {
176 return; /* jitterbug 135, fix for multiprocessor machines */
177 }
178
179 #if defined (WIN32)
180 #ifdef _DEBUG
181 if (mutex == &gGlobalMutex) {
182 gRecursionCount--;
183 U_ASSERT(gRecursionCount == 0);
184 }
185 #endif /*_DEBUG*/
186 LeaveCriticalSection((CRITICAL_SECTION*)*mutex);
187
188 #elif defined (POSIX)
189 pthread_mutex_unlock((pthread_mutex_t*)*mutex);
190
191 #ifdef POSIX_DEBUG_REENTRANCY
192 if (mutex == &gGlobalMutex) {
193 gInMutex = FALSE;
194 }
195 #endif
196
197 #endif
198 #endif /* ICU_USE_THREADS == 1 */
199 }
200
201
202
203 /*
204 * umtx_raw_init Do the platform specific mutex allocation and initialization
205 */
206 #if (ICU_USE_THREADS == 1)
207 static UMTX umtx_raw_init(void *mem) {
208 #if defined (WIN32)
209 if (mem == NULL) {
210 mem = uprv_malloc(sizeof(CRITICAL_SECTION));
211 if (mem == NULL) {return NULL;}
212 }
213 InitializeCriticalSection((CRITICAL_SECTION*)mem);
214 #elif defined( POSIX )
215 if (mem == NULL) {
216 mem = uprv_malloc(sizeof(pthread_mutex_t));
217 if (mem == NULL) {return NULL;}
218 }
219 # if defined (HPUX_CMA)
220 pthread_mutex_init((pthread_mutex_t*)mem, pthread_mutexattr_default);
221 # else
222 pthread_mutex_init((pthread_mutex_t*)mem, NULL);
223 # endif
224 #endif
225 return (UMTX *)mem;
226 }
227 #endif /* ICU_USE_THREADS */
228
229
230 U_CAPI void U_EXPORT2
231 umtx_init(UMTX *mutex)
232 {
233 #if (ICU_USE_THREADS == 1)
234
235 if (mutex == NULL) /* initialize the global mutex */
236 {
237 /* Note: The initialization of the global mutex is NOT thread safe. */
238 if (gGlobalMutex != NULL) {
239 return;
240 }
241 gGlobalMutex = umtx_raw_init(&gPlatformMutex);
242
243 # ifdef POSIX_DEBUG_REENTRANCY
244 gInMutex = FALSE;
245 # endif
246 #ifdef _DEBUG
247 gRecursionCount = 0;
248 #endif
249
250 #ifdef POSIX
251 umtx_raw_init(&gIncDecMutex);
252 #endif
253
254 } else {
255 /* Not the global mutex.
256 * Thread safe initialization, using the global mutex.
257 */
258 UBool isInitialized;
259 UMTX tMutex = NULL;
260
261 umtx_lock(NULL);
262 isInitialized = (*mutex != NULL);
263 umtx_unlock(NULL);
264 if (isInitialized) {
265 return;
266 }
267
268 tMutex = umtx_raw_init(NULL);
269
270 umtx_lock(NULL);
271 if (*mutex == NULL) {
272 *mutex = tMutex;
273 tMutex = NULL;
274 }
275 umtx_unlock(NULL);
276
277 umtx_destroy(&tMutex); /* NOP if (tmutex == NULL) */
278 }
279 #endif /* ICU_USE_THREADS==1 */
280 }
281
282 U_CAPI void U_EXPORT2
283 umtx_destroy(UMTX *mutex) {
284 #if (ICU_USE_THREADS == 1)
285 if (mutex == NULL) /* destroy the global mutex */
286 {
287 mutex = &gGlobalMutex;
288 }
289
290 if (*mutex == NULL) /* someone already did it. */
291 return;
292
293 #if defined (WIN32)
294 DeleteCriticalSection((CRITICAL_SECTION*)*mutex);
295
296 #elif defined (POSIX)
297 pthread_mutex_destroy((pthread_mutex_t*)*mutex);
298
299 #endif
300
301 if (*mutex != gGlobalMutex)
302 {
303 uprv_free(*mutex);
304 }
305
306 *mutex = NULL;
307 #endif /* ICU_USE_THREADS==1 */
308 }
309
310
311 #if (ICU_USE_THREADS == 1)
312
313
314 /*
315 * umtx_atomic_inc
316 * umtx_atomic_dec
317 */
318
319 #if defined (WIN32)
320 /*
321 * Win32 - use the Windows API functions for atomic increment and decrement.
322 */
323 U_CAPI int32_t U_EXPORT2
324 umtx_atomic_inc(int32_t *p)
325 {
326 return InterlockedIncrement(p);
327 }
328
329 U_CAPI int32_t U_EXPORT2
330 umtx_atomic_dec(int32_t *p)
331 {
332 return InterlockedDecrement(p);
333 }
334
335 #elif defined (POSIX)
336 /*
337 * POSIX platforms without specific atomic operations. Use a posix mutex
338 * to protect the increment and decrement.
339 * The IncDecMutex is in static storage so we don't have to come back and delete it
340 * when the process exits.
341 */
342
343 U_CAPI int32_t U_EXPORT2
344 umtx_atomic_inc(int32_t *p)
345 {
346 int32_t retVal;
347
348 pthread_mutex_lock(&gIncDecMutex);
349 retVal = ++(*p);
350 pthread_mutex_unlock(&gIncDecMutex);
351 return retVal;
352 }
353
354
355 U_CAPI int32_t U_EXPORT2
356 umtx_atomic_dec(int32_t *p)
357 {
358 int32_t retVal;
359
360 pthread_mutex_lock(&gIncDecMutex);
361 retVal = --(*p);
362 pthread_mutex_unlock(&gIncDecMutex);
363 return retVal;
364 }
365
366
367 #else
368
369 /* No recognized platform. */
370 #error No atomic increment and decrement defined for this platform. \
371 Either use the --disable-threads configure option, or define those functions in this file.
372
373 #endif /* Platform selection for atomic_inc and dec. */
374
375
376 #else /* (ICU_USE_THREADS == 0) */
377
378 /* Threads disabled here */
379
380 U_CAPI int32_t U_EXPORT2
381 umtx_atomic_inc(int32_t *p) {
382 return ++(*p);
383 }
384
385 U_CAPI int32_t U_EXPORT2
386 umtx_atomic_dec(int32_t *p) {
387 return --(*p);
388 }
389
390 #endif /* (ICU_USE_THREADS == 1) */
391
392
393
394