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 */
158static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval);
159typedef 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 */
168struct 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)
196static UMTX  globalUMTX;
197static ICUMutex globalMutex = {&globalUMTX, FALSE, NULL, 0, PLATFORM_MUTEX_INITIALIZER, NULL};
198static UMTX  globalUMTX = &globalMutex;
199#else
200static 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 */
212static 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 */
220static UMtxInitFn    *pMutexInitFn    = NULL;
221static UMtxFn        *pMutexDestroyFn = NULL;
222static UMtxFn        *pMutexLockFn    = NULL;
223static UMtxFn        *pMutexUnlockFn  = NULL;
224static const void    *gMutexContext   = NULL;
225
226
227/*
228 *   umtx_lock
229 */
230U_CAPI void  U_EXPORT2
231umtx_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 */
267U_CAPI void  U_EXPORT2
268umtx_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 */
297static 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 */
319static 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
333U_CAPI void  U_EXPORT2
334umtx_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 */
378U_CAPI void  U_EXPORT2
379umtx_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
420U_CAPI void U_EXPORT2
421u_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 */
469static 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.  */
500static UMtxAtomicFn  *pIncFn = NULL;
501static UMtxAtomicFn  *pDecFn = NULL;
502static const void *gIncDecContext  = NULL;
503
504static UMTX    gIncDecMutex = NULL;
505
506U_CAPI int32_t U_EXPORT2
507umtx_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
530U_CAPI int32_t U_EXPORT2
531umtx_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
556U_CAPI void U_EXPORT2
557u_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 */
595U_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
636