]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/umutex.c
ICU-511.25.tar.gz
[apple/icu.git] / icuSources / common / umutex.c
diff --git a/icuSources/common/umutex.c b/icuSources/common/umutex.c
deleted file mode 100644 (file)
index 1c1d1d3..0000000
+++ /dev/null
@@ -1,621 +0,0 @@
-/*
-******************************************************************************
-*
-*   Copyright (C) 1997-2011, International Business Machines
-*   Corporation and others.  All Rights Reserved.
-*
-******************************************************************************
-*
-* File umutex.c
-*
-* Modification History:
-*
-*   Date        Name        Description
-*   04/02/97    aliu        Creation.
-*   04/07/99    srl         updated
-*   05/13/99    stephen     Changed to umutex (from cmutex).
-*   11/22/99    aliu        Make non-global mutex autoinitialize [j151]
-******************************************************************************
-*/
-
-#include "unicode/utypes.h"
-#include "uassert.h"
-#include "ucln_cmn.h"
-
-/*
- * ICU Mutex wrappers.  Wrap operating system mutexes, giving the rest of ICU a
- * platform independent set of mutex operations.  For internal ICU use only.
- */
-
-#if U_PLATFORM_HAS_WIN32_API
-    /* Prefer native Windows APIs even if POSIX is implemented (i.e., on Cygwin). */
-#   undef POSIX
-#elif U_PLATFORM_IMPLEMENTS_POSIX
-#   define POSIX
-#else
-#   undef POSIX
-#endif
-
-#if defined(POSIX) && (ICU_USE_THREADS==1)
-# include <pthread.h> /* must be first, so that we get the multithread versions of things. */
-
-#endif /* POSIX && (ICU_USE_THREADS==1) */
-
-#if U_PLATFORM_HAS_WIN32_API
-# define WIN32_LEAN_AND_MEAN
-# define VC_EXTRALEAN
-# define NOUSER
-# define NOSERVICE
-# define NOIME
-# define NOMCX
-# include <windows.h>
-#endif
-
-#include "umutex.h"
-#include "cmemory.h"
-
-/*
- * A note on ICU Mutex Initialization and ICU startup:
- *
- *   ICU mutexes, as used through the rest of the ICU code, are self-initializing.
- *   To make this work, ICU uses the _ICU GLobal Mutex_ to synchronize the lazy init
- *   of other ICU mutexes.  For the global mutex itself, we need some other mechanism
- *   to safely initialize it on first use.  This becomes important when two or more
- *   threads are more or less simultaenously the first to use ICU in a process, and
- *   are racing into the mutex initialization code.
- *
- *
- *   The solution for the global mutex init is platform dependent.
- *   On POSIX systems, plain C-style initialization can be used on a mutex, with the 
- *   macro PTHREAD_MUTEX_INITIALIZER.  The mutex is then ready for use, without
- *   first calling pthread_mutex_init().
- *
- *   Windows has no equivalent statically initialized mutex or CRITICAL SECION.
- *   InitializeCriticalSection() must be called.  If the global mutex does not
- *   appear to be initialized, a thread will create and initialize a new
- *   CRITICAL_SECTION, then use a Windows InterlockedCompareAndExchange to
- *   swap it in as the global mutex while avoid problems with race conditions.
- */ 
-
-/* On WIN32 mutexes are reentrant.  On POSIX platforms they are not, and a deadlock
- *  will occur if a thread attempts to acquire a mutex it already has locked.
- *  ICU mutexes (in debug builds) include checking code that will cause an assertion
- *  failure if a mutex is reentered.  If you are having deadlock problems
- *  on a POSIX machine, debugging may be easier on Windows.
- */
-
-
-#if (ICU_USE_THREADS == 0)
-#define MUTEX_TYPE void *
-#define PLATFORM_MUTEX_INIT(m) 
-#define PLATFORM_MUTEX_LOCK(m) 
-#define PLATFORM_MUTEX_UNLOCK(m) 
-#define PLATFORM_MUTEX_DESTROY(m) 
-#define PLATFORM_MUTEX_INITIALIZER NULL
-#define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
-            mutexed_compare_and_swap(dest, newval, oldval)
-
-
-#elif U_PLATFORM_HAS_WIN32_API
-#define MUTEX_TYPE CRITICAL_SECTION
-#define PLATFORM_MUTEX_INIT(m) InitializeCriticalSection(m)
-#define PLATFORM_MUTEX_LOCK(m) EnterCriticalSection(m)
-#define PLATFORM_MUTEX_UNLOCK(m) LeaveCriticalSection(m)
-#define PLATFORM_MUTEX_DESTROY(m) DeleteCriticalSection(m)
-#define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
-            InterlockedCompareExchangePointer(dest, newval, oldval)
-
-
-#elif defined(POSIX)
-#define MUTEX_TYPE pthread_mutex_t   
-#define PLATFORM_MUTEX_INIT(m) pthread_mutex_init(m, NULL)
-#define PLATFORM_MUTEX_LOCK(m) pthread_mutex_lock(m)
-#define PLATFORM_MUTEX_UNLOCK(m) pthread_mutex_unlock(m)
-#define PLATFORM_MUTEX_DESTROY(m) pthread_mutex_destroy(m)
-#define PLATFORM_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
-#if (U_HAVE_GCC_ATOMICS == 1)
-#define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
-            __sync_val_compare_and_swap(dest, oldval, newval)
-#else
-#define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
-            mutexed_compare_and_swap(dest, newval, oldval)
-#endif
-
-
-#else   
-/* Unknown platform.  Note that user can still set mutex functions at run time. */
-#define MUTEX_TYPE void *
-#define PLATFORM_MUTEX_INIT(m) 
-#define PLATFORM_MUTEX_LOCK(m)
-#define PLATFORM_MUTEX_UNLOCK(m) 
-#define PLATFORM_MUTEX_DESTROY(m) 
-#define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
-            mutexed_compare_and_swap(dest, newval, oldval)
-
-#endif
-
-/*  Forward declarations */
-static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval);
-typedef struct ICUMutex ICUMutex;
-
-/*
- * ICUMutex   One of these is set up for each UMTX that is used by other ICU code.
- *            The opaque UMTX points to the corresponding ICUMutex struct.
- *            
- *            Because the total number of ICU mutexes is quite small, no effort has
- *            been made to squeeze every byte out of this struct.
- */
-struct ICUMutex {
-    UMTX        *owner;             /* Points back to the UMTX corrsponding to this   */
-                                    /*    ICUMutex object.                            */
-    
-    UBool        heapAllocated;     /* Set if this ICUMutex is heap allocated, and    */
-                                    /*   will need to be deleted.  The global mutex   */
-                                    /*   is static on POSIX platforms; all others     */
-                                    /*   will be heap allocated.                      */
-
-    ICUMutex    *next;              /* All ICUMutexes are chained into a list so that  */
-                                    /*   they can be found and deleted by u_cleanup(). */
-
-    int32_t      recursionCount;    /* For debugging, detect recursive mutex locks.    */
-
-    MUTEX_TYPE   platformMutex;     /* The underlying OS mutex being wrapped.          */
-
-    UMTX         userMutex;         /* For use with u_setMutexFunctions operations,    */
-                                    /*    corresponds to platformMutex.                */
-};
-
-
-/*   The global ICU mutex.
- *   For POSIX platforms, it gets a C style initialization, and is ready to use
- *        at program startup.
- *   For Windows, it will be lazily instantiated on first use.
- */
-
-#if defined(POSIX)
-static UMTX  globalUMTX;
-static ICUMutex globalMutex = {&globalUMTX, FALSE, NULL, 0, PLATFORM_MUTEX_INITIALIZER, NULL};
-static UMTX  globalUMTX = &globalMutex;
-#else
-static UMTX  globalUMTX = NULL;
-#endif
-
-/* Head of the list of all ICU mutexes.
- * Linked list is through ICUMutex::next
- * Modifications to the list are synchronized with the global mutex.
- * The list is used by u_cleanup(), which needs to dispose of all of the ICU mutexes.
- *
- * The statically initialized global mutex on POSIX platforms does not get added to this
- * mutex list, but that's not a problem - the global mutex gets special handling
- * during u_cleanup().
- */
-static ICUMutex *mutexListHead;
-
-
-/*
- *  User mutex implementation functions.  If non-null, call back to these rather than
- *  directly using the system (Posix or Windows) APIs.  See u_setMutexFunctions().
- *    (declarations are in uclean.h)
- */
-static UMtxInitFn    *pMutexInitFn    = NULL;
-static UMtxFn        *pMutexDestroyFn = NULL;
-static UMtxFn        *pMutexLockFn    = NULL;
-static UMtxFn        *pMutexUnlockFn  = NULL;
-static const void    *gMutexContext   = NULL;
-
-
-/*
- *   umtx_lock
- */
-U_CAPI void  U_EXPORT2
-umtx_lock(UMTX *mutex)
-{
-    ICUMutex *m;
-
-    if (mutex == NULL) {
-        mutex = &globalUMTX;
-    }
-    m = (ICUMutex *)*mutex;
-    if (m == NULL) {
-        /* See note on lazy initialization, above.  We can get away with it here, with mutexes,
-         * where we couldn't with normal user level data.
-         */
-        umtx_init(mutex);    
-        m = (ICUMutex *)*mutex;
-    }
-    U_ASSERT(m->owner == mutex);
-
-    if (pMutexLockFn != NULL) {
-        (*pMutexLockFn)(gMutexContext, &m->userMutex);
-    } else {
-        PLATFORM_MUTEX_LOCK(&m->platformMutex);
-    }
-
-#if defined(U_DEBUG)
-    m->recursionCount++;              /* Recursion causes deadlock on Unixes.               */
-    U_ASSERT(m->recursionCount == 1); /* Recursion detection works on Windows.              */
-                                      /* Assertion failure on non-Windows indicates a       */
-                                      /*   problem with the mutex implementation itself.    */
-#endif
-}
-
-
-
-/*
- * umtx_unlock
- */
-U_CAPI void  U_EXPORT2
-umtx_unlock(UMTX* mutex)
-{
-    ICUMutex *m;
-    if(mutex == NULL) {
-        mutex = &globalUMTX;
-    }
-    m = (ICUMutex *)*mutex;
-    if (m == NULL) {
-        U_ASSERT(FALSE);  /* This mutex is not initialized.     */
-        return; 
-    }
-    U_ASSERT(m->owner == mutex);
-
-#if defined (U_DEBUG)
-    m->recursionCount--;
-    U_ASSERT(m->recursionCount == 0);  /* Detect unlock of an already unlocked mutex */
-#endif
-
-    if (pMutexUnlockFn) {
-        (*pMutexUnlockFn)(gMutexContext, &m->userMutex);
-    } else {
-        PLATFORM_MUTEX_UNLOCK(&m->platformMutex);
-    }
-}
-
-
-/* umtx_ct   Allocate and initialize a new ICUMutex.
- *           If a non-null pointer is supplied, initialize an existing ICU Mutex.
- */
-static ICUMutex *umtx_ct(ICUMutex *m) {
-    if (m == NULL) {
-        m = (ICUMutex *)uprv_malloc(sizeof(ICUMutex));
-        m->heapAllocated = TRUE;
-    }
-    m->next = NULL;    /* List of mutexes is maintained at a higher level.  */
-    m->recursionCount = 0;
-    m->userMutex = NULL;
-    if (pMutexInitFn != NULL) {
-        UErrorCode status = U_ZERO_ERROR;
-        (*pMutexInitFn)(gMutexContext, &m->userMutex, &status);
-        U_ASSERT(U_SUCCESS(status));
-    } else {
-        PLATFORM_MUTEX_INIT(&m->platformMutex);
-    }
-    return m;
-}
-
-
-/* umtx_dt   Delete a ICUMutex.  Destroy the underlying OS Platform mutex.
- *           Does not touch the linked list of ICU Mutexes.
- */
-static void umtx_dt(ICUMutex *m) {
-    if (pMutexDestroyFn != NULL) {
-        (*pMutexDestroyFn)(gMutexContext, &m->userMutex);
-        m->userMutex = NULL;
-    } else {
-        PLATFORM_MUTEX_DESTROY(&m->platformMutex);
-    }
-
-    if (m->heapAllocated) {
-        uprv_free(m);
-    }
-}
-    
-
-U_CAPI void  U_EXPORT2
-umtx_init(UMTX *mutex) {
-    ICUMutex *m = NULL;
-    void *originalValue;
-
-    if (*mutex != NULL) {
-        /* Mutex is already initialized.  
-         * Multiple umtx_init()s of a UMTX by other ICU code are explicitly permitted.
-         */
-        return;
-    }
-#if defined(POSIX)
-    if (mutex == &globalUMTX) {
-        m = &globalMutex;
-    }
-#endif
-
-    m = umtx_ct(m);
-    originalValue = SYNC_COMPARE_AND_SWAP(mutex, NULL, m);
-    if (originalValue != NULL) {
-        umtx_dt(m);
-        return;
-    }
-
-    m->owner = mutex;
-
-    /* Hook the new mutex into the list of all ICU mutexes, so that we can find and
-     * delete it for u_cleanup().
-     */
-
-    umtx_lock(NULL);
-    m->next = mutexListHead;
-    mutexListHead = m;
-    umtx_unlock(NULL);
-    return;
-}
-
-
-/*
- *  umtx_destroy.    Un-initialize a mutex, releasing any underlying resources
- *                   that it may be holding.  Destroying an already destroyed
- *                   mutex has no effect.  Unlike umtx_init(), this function
- *                   is not thread safe;  two threads must not concurrently try to
- *                   destroy the same mutex.
- */                  
-U_CAPI void  U_EXPORT2
-umtx_destroy(UMTX *mutex) {
-    ICUMutex *m;
-    
-    /* No one should be deleting the global ICU mutex. 
-     *   (u_cleanup() does delete it, but does so explicitly, not by passing NULL)
-     */
-    U_ASSERT(mutex != NULL);
-    if (mutex == NULL) { 
-        return;
-    }
-    
-    m = (ICUMutex *)*mutex;
-    if (m == NULL) {  /* Mutex not initialized, or already destroyed.  */
-        return;
-    }
-
-    U_ASSERT(m->owner == mutex);
-    if (m->owner != mutex) {
-        return;
-    }
-
-    /* Remove this mutex from the linked list of mutexes.  */
-    umtx_lock(NULL);
-    if (mutexListHead == m) {
-        mutexListHead = m->next;
-    } else {
-        ICUMutex *prev;
-        for (prev = mutexListHead; prev!=NULL && prev->next!=m; prev = prev->next);
-            /*  Empty for loop body */
-        if (prev != NULL) {
-            prev->next = m->next;
-        }
-    }
-    umtx_unlock(NULL);
-
-    umtx_dt(m);        /* Delete the internal ICUMutex   */
-    *mutex = NULL;     /* Clear the caller's UMTX        */
-}
-
-
-
-U_CAPI void U_EXPORT2 
-u_setMutexFunctions(const void *context, UMtxInitFn *i, UMtxFn *d, UMtxFn *l, UMtxFn *u,
-                    UErrorCode *status) {
-    if (U_FAILURE(*status)) {
-        return;
-    }
-
-    /* Can not set a mutex function to a NULL value  */
-    if (i==NULL || d==NULL || l==NULL || u==NULL) {
-        *status = U_ILLEGAL_ARGUMENT_ERROR;
-        return;
-    }
-
-    /* If ICU is not in an initial state, disallow this operation. */
-    if (cmemory_inUse()) {
-        *status = U_INVALID_STATE_ERROR;
-        return;
-    }
-    
-    /* Kill any existing global mutex.  POSIX platforms have a global mutex
-     * even before any other part of ICU is initialized.
-     */
-    umtx_destroy(&globalUMTX);
-
-    /* Swap in the mutex function pointers.  */
-    pMutexInitFn    = i;
-    pMutexDestroyFn = d;
-    pMutexLockFn    = l;
-    pMutexUnlockFn  = u;
-    gMutexContext   = context;
-
-#if defined (POSIX) 
-    /* POSIX platforms must have a pre-initialized global mutex 
-     * to allow other mutexes to initialize safely. */
-    umtx_init(&globalUMTX);
-#endif
-}
-
-
-/*   synchronized compare and swap function, for use when OS or compiler built-in
- *   equivalents aren't available.
- *
- *   This operation relies on the ICU global mutex for synchronization.
- *
- *   There are two cases where this function can be entered when the global mutex is not
- *   yet initialized - at the end  u_cleanup(), and at the end of u_setMutexFunctions, both
- *   of which re-init the global mutex.  But neither function is thread-safe, so the lack of
- *   synchronization at these points doesn't matter.
- */
-static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval) {
-    void *temp;
-    UBool needUnlock = FALSE;
-
-    if (globalUMTX != NULL) {
-        umtx_lock(&globalUMTX);
-        needUnlock = TRUE;
-    }
-
-    temp = *dest;
-    if (temp == oldval) {
-        *dest = newval;
-    }
-    
-    if (needUnlock) {
-        umtx_unlock(&globalUMTX);
-    }
-    return temp;
-}
-
-
-
-/*-----------------------------------------------------------------
- *
- *  Atomic Increment and Decrement
- *     umtx_atomic_inc
- *     umtx_atomic_dec
- *
- *----------------------------------------------------------------*/
-
-/* Pointers to user-supplied inc/dec functions.  Null if no funcs have been set.  */
-static UMtxAtomicFn  *pIncFn = NULL;
-static UMtxAtomicFn  *pDecFn = NULL;
-static const void *gIncDecContext  = NULL;
-
-static UMTX    gIncDecMutex = NULL;
-
-U_CAPI int32_t U_EXPORT2
-umtx_atomic_inc(int32_t *p)  {
-    int32_t retVal;
-    if (pIncFn) {
-        retVal = (*pIncFn)(gIncDecContext, p);
-    } else {
-        #if !ICU_USE_THREADS
-            /* ICU thread support compiled out. */
-            retVal = ++(*p);
-        #elif U_PLATFORM_HAS_WIN32_API
-            retVal = InterlockedIncrement((LONG*)p);
-        #elif defined(USE_MAC_OS_ATOMIC_INCREMENT)
-            retVal = OSAtomicIncrement32Barrier(p);
-        #elif (U_HAVE_GCC_ATOMICS == 1)
-            retVal = __sync_add_and_fetch(p, 1);
-        #elif defined (POSIX)
-            umtx_lock(&gIncDecMutex);
-            retVal = ++(*p);
-            umtx_unlock(&gIncDecMutex);
-        #else
-            /* Unknown Platform. */
-            retVal = ++(*p);
-        #endif
-    }
-    return retVal;
-}
-
-U_CAPI int32_t U_EXPORT2
-umtx_atomic_dec(int32_t *p) {
-    int32_t retVal;
-    if (pDecFn) {
-        retVal = (*pDecFn)(gIncDecContext, p);
-    } else {
-        #if !ICU_USE_THREADS
-            /* ICU thread support compiled out. */
-            retVal = --(*p);
-        #elif U_PLATFORM_HAS_WIN32_API
-            retVal = InterlockedDecrement((LONG*)p);
-        #elif defined(USE_MAC_OS_ATOMIC_INCREMENT)
-            retVal = OSAtomicDecrement32Barrier(p);
-        #elif (U_HAVE_GCC_ATOMICS == 1)
-            retVal = __sync_sub_and_fetch(p, 1);
-        #elif defined (POSIX)
-            umtx_lock(&gIncDecMutex);
-            retVal = --(*p);
-            umtx_unlock(&gIncDecMutex);
-        #else
-            /* Unknown Platform. */
-            retVal = --(*p);
-        #endif
-    }
-    return retVal;
-}
-
-
-
-U_CAPI void U_EXPORT2
-u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *ip, UMtxAtomicFn *dp,
-                                UErrorCode *status) {
-    if (U_FAILURE(*status)) {
-        return;
-    }
-    /* Can not set a mutex function to a NULL value  */
-    if (ip==NULL || dp==NULL) {
-        *status = U_ILLEGAL_ARGUMENT_ERROR;
-        return;
-    }
-    /* If ICU is not in an initial state, disallow this operation. */
-    if (cmemory_inUse()) {
-        *status = U_INVALID_STATE_ERROR;
-        return;
-    }
-
-    pIncFn = ip;
-    pDecFn = dp;
-    gIncDecContext = context;
-
-#if U_DEBUG
-    {
-        int32_t   testInt = 0;
-        U_ASSERT(umtx_atomic_inc(&testInt) == 1);     /* Sanity Check.    Do the functions work at all? */
-        U_ASSERT(testInt == 1);
-        U_ASSERT(umtx_atomic_dec(&testInt) == 0);
-        U_ASSERT(testInt == 0);
-    }
-#endif
-}
-
-
-
-/*
- *  Mutex Cleanup Function
- *
- *      Destroy the global mutex(es), and reset the mutex function callback pointers.
- */
-U_CFUNC UBool umtx_cleanup(void) {
-    ICUMutex *thisMutex = NULL;
-    ICUMutex *nextMutex = NULL;
-
-    /* Extra, do-nothing function call to suppress compiler warnings on platforms where
-     *   mutexed_compare_and_swap is not otherwise used.  */
-    mutexed_compare_and_swap(&globalUMTX, NULL, NULL);
-
-    /* Delete all of the ICU mutexes.  Do the global mutex last because it is used during
-     * the umtx_destroy operation of other mutexes.
-     */
-    for (thisMutex=mutexListHead; thisMutex!=NULL; thisMutex=nextMutex) {
-        UMTX *umtx = thisMutex->owner;
-        nextMutex = thisMutex->next;
-        U_ASSERT(*umtx = (void *)thisMutex);
-        if (umtx != &globalUMTX) {
-            umtx_destroy(umtx);
-        }
-    }
-    umtx_destroy(&globalUMTX);
-        
-    pMutexInitFn    = NULL;
-    pMutexDestroyFn = NULL;
-    pMutexLockFn    = NULL;
-    pMutexUnlockFn  = NULL;
-    gMutexContext   = NULL;
-    pIncFn          = NULL;
-    pDecFn          = NULL;
-    gIncDecContext  = NULL;
-    gIncDecMutex    = NULL;
-
-#if defined (POSIX) 
-    /* POSIX platforms must come out of u_cleanup() with a functioning global mutex 
-     * to permit the safe resumption of use of ICU in multi-threaded environments. 
-     */
-    umtx_init(&globalUMTX);
-#endif
-    return TRUE;
-}
-
-