| Index: icu51/source/common/umutex.cpp
|
| ===================================================================
|
| --- icu51/source/common/umutex.cpp (revision 0)
|
| +++ icu51/source/common/umutex.cpp (revision 0)
|
| @@ -0,0 +1,483 @@
|
| +/*
|
| +******************************************************************************
|
| +*
|
| +* Copyright (C) 1997-2012, International Business Machines
|
| +* Corporation and others. All Rights Reserved.
|
| +*
|
| +******************************************************************************
|
| +*
|
| +* File umutex.cpp
|
| +*
|
| +* 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)
|
| +# include <pthread.h> /* must be first, so that we get the multithread versions of things. */
|
| +#endif /* POSIX */
|
| +
|
| +#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"
|
| +
|
| +#if U_PLATFORM_HAS_WIN32_API
|
| +#define SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
|
| + InterlockedCompareExchangePointer(dest, newval, oldval)
|
| +
|
| +#elif defined(POSIX)
|
| +#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 SYNC_COMPARE_AND_SWAP(dest, oldval, newval) \
|
| + mutexed_compare_and_swap(dest, newval, oldval)
|
| +#endif
|
| +
|
| +static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval);
|
| +
|
| +// The ICU global mutex. Used when ICU implementation code passes NULL for the mutex pointer.
|
| +static UMutex globalMutex = U_MUTEX_INITIALIZER;
|
| +
|
| +// Implementation mutex. Used for compare & swap when no intrinsic is available, and
|
| +// for safe initialization of user defined mutexes.
|
| +static UMutex implMutex = U_MUTEX_INITIALIZER;
|
| +
|
| +// List of all user mutexes that have been initialized.
|
| +// Used to allow us to destroy them when cleaning up ICU.
|
| +// Normal platform mutexes are not kept track of in this way - they survive until the process is shut down.
|
| +// Normal platfrom mutexes don't allocate storage, so not cleaning them up won't trigger memory leak complaints.
|
| +//
|
| +// Note: putting this list in allocated memory would be awkward to arrange, because memory allocations
|
| +// are used as a flag to indicate that ICU has been initialized, and setting other ICU
|
| +// override functions will no longer work.
|
| +//
|
| +static const int MUTEX_LIST_LIMIT = 100;
|
| +static UMutex *gMutexList[MUTEX_LIST_LIMIT];
|
| +static int gMutexListSize = 0;
|
| +
|
| +
|
| +/*
|
| + * 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;
|
| +
|
| +
|
| +// Clean up (undo) the effects of u_setMutexFunctions().
|
| +//
|
| +static void usrMutexCleanup() {
|
| + if (pMutexDestroyFn != NULL) {
|
| + for (int i = 0; i < gMutexListSize; i++) {
|
| + UMutex *m = gMutexList[i];
|
| + U_ASSERT(m->fInitialized);
|
| + (*pMutexDestroyFn)(gMutexContext, &m->fUserMutex);
|
| + m->fInitialized = FALSE;
|
| + }
|
| + (*pMutexDestroyFn)(gMutexContext, &globalMutex.fUserMutex);
|
| + (*pMutexDestroyFn)(gMutexContext, &implMutex.fUserMutex);
|
| + }
|
| + gMutexListSize = 0;
|
| + pMutexInitFn = NULL;
|
| + pMutexDestroyFn = NULL;
|
| + pMutexLockFn = NULL;
|
| + pMutexUnlockFn = NULL;
|
| + gMutexContext = NULL;
|
| +}
|
| +
|
| +
|
| +/*
|
| + * User mutex lock.
|
| + *
|
| + * User mutexes need to be initialized before they can be used. We use the impl mutex
|
| + * to synchronize the initialization check. This could be sped up on platforms that
|
| + * support alternate ways to safely check the initialization flag.
|
| + *
|
| + */
|
| +static void usrMutexLock(UMutex *mutex) {
|
| + UErrorCode status = U_ZERO_ERROR;
|
| + if (!(mutex == &implMutex || mutex == &globalMutex)) {
|
| + umtx_lock(&implMutex);
|
| + if (!mutex->fInitialized) {
|
| + (*pMutexInitFn)(gMutexContext, &mutex->fUserMutex, &status);
|
| + U_ASSERT(U_SUCCESS(status));
|
| + mutex->fInitialized = TRUE;
|
| + U_ASSERT(gMutexListSize < MUTEX_LIST_LIMIT);
|
| + if (gMutexListSize < MUTEX_LIST_LIMIT) {
|
| + gMutexList[gMutexListSize] = mutex;
|
| + ++gMutexListSize;
|
| + }
|
| + }
|
| + umtx_unlock(&implMutex);
|
| + }
|
| + (*pMutexLockFn)(gMutexContext, &mutex->fUserMutex);
|
| +}
|
| +
|
| +
|
| +
|
| +#if defined(POSIX)
|
| +
|
| +//
|
| +// POSIX implementation of UMutex.
|
| +//
|
| +// Each UMutex has a corresponding pthread_mutex_t.
|
| +// All are statically initialized and ready for use.
|
| +// There is no runtime mutex initialization code needed.
|
| +
|
| +U_CAPI void U_EXPORT2
|
| +umtx_lock(UMutex *mutex) {
|
| + if (mutex == NULL) {
|
| + mutex = &globalMutex;
|
| + }
|
| + if (pMutexLockFn) {
|
| + usrMutexLock(mutex);
|
| + } else {
|
| + #if U_DEBUG
|
| + // #if to avoid unused variable warnings in non-debug builds.
|
| + int sysErr = pthread_mutex_lock(&mutex->fMutex);
|
| + U_ASSERT(sysErr == 0);
|
| + #else
|
| + pthread_mutex_lock(&mutex->fMutex);
|
| + #endif
|
| + }
|
| +}
|
| +
|
| +
|
| +U_CAPI void U_EXPORT2
|
| +umtx_unlock(UMutex* mutex)
|
| +{
|
| + if (mutex == NULL) {
|
| + mutex = &globalMutex;
|
| + }
|
| + if (pMutexUnlockFn) {
|
| + (*pMutexUnlockFn)(gMutexContext, &mutex->fUserMutex);
|
| + } else {
|
| + #if U_DEBUG
|
| + // #if to avoid unused variable warnings in non-debug builds.
|
| + int sysErr = pthread_mutex_unlock(&mutex->fMutex);
|
| + U_ASSERT(sysErr == 0);
|
| + #else
|
| + pthread_mutex_unlock(&mutex->fMutex);
|
| + #endif
|
| + }
|
| +}
|
| +
|
| +#elif U_PLATFORM_HAS_WIN32_API
|
| +//
|
| +// Windows implementation of UMutex.
|
| +//
|
| +// Each UMutex has a corresponding Windows CRITICAL_SECTION.
|
| +// CRITICAL_SECTIONS must be initialized before use. This is done
|
| +// with a InitOnceExcuteOnce operation.
|
| +//
|
| +// InitOnceExecuteOnce was introduced with Windows Vista. For now ICU
|
| +// must support Windows XP, so we roll our own. ICU will switch to the
|
| +// native Windows InitOnceExecuteOnce when possible.
|
| +
|
| +typedef UBool (*U_PINIT_ONCE_FN) (
|
| + U_INIT_ONCE *initOnce,
|
| + void *parameter,
|
| + void **context
|
| +);
|
| +
|
| +UBool u_InitOnceExecuteOnce(
|
| + U_INIT_ONCE *initOnce,
|
| + U_PINIT_ONCE_FN initFn,
|
| + void *parameter,
|
| + void **context) {
|
| + for (;;) {
|
| + long previousState = InterlockedCompareExchange(
|
| + &initOnce->fState, // Destination,
|
| + 1, // Exchange Value
|
| + 0); // Compare value
|
| + if (previousState == 2) {
|
| + // Initialization was already completed.
|
| + if (context != NULL) {
|
| + *context = initOnce->fContext;
|
| + }
|
| + return TRUE;
|
| + }
|
| + if (previousState == 1) {
|
| + // Initialization is in progress in some other thread.
|
| + // Loop until it completes.
|
| + Sleep(1);
|
| + continue;
|
| + }
|
| +
|
| + // Initialization needed. Execute the callback function to do it.
|
| + U_ASSERT(previousState == 0);
|
| + U_ASSERT(initOnce->fState == 1);
|
| + UBool success = (*initFn)(initOnce, parameter, &initOnce->fContext);
|
| + U_ASSERT(success); // ICU is not supporting the failure case.
|
| +
|
| + // Assign the state indicating that initialization has completed.
|
| + // Using InterlockedCompareExchange to do it ensures that all
|
| + // threads will have a consistent view of memory.
|
| + previousState = InterlockedCompareExchange(&initOnce->fState, 2, 1);
|
| + U_ASSERT(previousState == 1);
|
| + // Next loop iteration will see the initialization and return.
|
| + }
|
| +};
|
| +
|
| +static UBool winMutexInit(U_INIT_ONCE *initOnce, void *param, void **context) {
|
| + UMutex *mutex = static_cast<UMutex *>(param);
|
| + U_ASSERT(sizeof(CRITICAL_SECTION) <= sizeof(mutex->fCS));
|
| + InitializeCriticalSection((CRITICAL_SECTION *)mutex->fCS);
|
| + return TRUE;
|
| +}
|
| +
|
| +/*
|
| + * umtx_lock
|
| + */
|
| +U_CAPI void U_EXPORT2
|
| +umtx_lock(UMutex *mutex) {
|
| + if (mutex == NULL) {
|
| + mutex = &globalMutex;
|
| + }
|
| + if (pMutexLockFn) {
|
| + usrMutexLock(mutex);
|
| + } else {
|
| + u_InitOnceExecuteOnce(&mutex->fInitOnce, winMutexInit, mutex, NULL);
|
| + EnterCriticalSection((CRITICAL_SECTION *)mutex->fCS);
|
| + }
|
| +}
|
| +
|
| +U_CAPI void U_EXPORT2
|
| +umtx_unlock(UMutex* mutex)
|
| +{
|
| + if (mutex == NULL) {
|
| + mutex = &globalMutex;
|
| + }
|
| + if (pMutexUnlockFn) {
|
| + (*pMutexUnlockFn)(gMutexContext, &mutex->fUserMutex);
|
| + } else {
|
| + LeaveCriticalSection((CRITICAL_SECTION *)mutex->fCS);
|
| + }
|
| +}
|
| +
|
| +#endif // Windows Implementation
|
| +
|
| +
|
| +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;
|
| + }
|
| +
|
| + // Clean up any previously set user mutex functions.
|
| + // It's possible to call u_setMutexFunctions() more than once without without explicitly cleaning up,
|
| + // and the last call should take. Kind of a corner case, but it worked once, there is a test for
|
| + // it, so we keep it working. The global and impl mutexes will have been created by the
|
| + // previous u_setMutexFunctions(), and now need to be destroyed.
|
| +
|
| + usrMutexCleanup();
|
| +
|
| + /* Swap in the mutex function pointers. */
|
| + pMutexInitFn = i;
|
| + pMutexDestroyFn = d;
|
| + pMutexLockFn = l;
|
| + pMutexUnlockFn = u;
|
| + gMutexContext = context;
|
| + gMutexListSize = 0;
|
| +
|
| + /* Initialize the global and impl mutexes. Safe to do at this point because
|
| + * u_setMutexFunctions must be done in a single-threaded envioronment. Not thread safe.
|
| + */
|
| + (*pMutexInitFn)(gMutexContext, &globalMutex.fUserMutex, status);
|
| + globalMutex.fInitialized = TRUE;
|
| + (*pMutexInitFn)(gMutexContext, &implMutex.fUserMutex, status);
|
| + implMutex.fInitialized = TRUE;
|
| +}
|
| +
|
| +
|
| +
|
| +/* synchronized compare and swap function, for use when OS or compiler built-in
|
| + * equivalents aren't available.
|
| + */
|
| +static void *mutexed_compare_and_swap(void **dest, void *newval, void *oldval) {
|
| + umtx_lock(&implMutex);
|
| + void *temp = *dest;
|
| + if (temp == oldval) {
|
| + *dest = newval;
|
| + }
|
| + umtx_unlock(&implMutex);
|
| +
|
| + 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;
|
| +
|
| +#if defined (POSIX) && (U_HAVE_GCC_ATOMICS == 0)
|
| +static UMutex gIncDecMutex = U_MUTEX_INITIALIZER;
|
| +#endif
|
| +
|
| +U_CAPI int32_t U_EXPORT2
|
| +umtx_atomic_inc(int32_t *p) {
|
| + int32_t retVal;
|
| + if (pIncFn) {
|
| + retVal = (*pIncFn)(gIncDecContext, p);
|
| + } else {
|
| + #if 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 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
|
| + * Reset the mutex function callback pointers.
|
| + * Called from the global ICU u_cleanup() function.
|
| + */
|
| +U_CFUNC UBool umtx_cleanup(void) {
|
| + /* Extra, do-nothing function call to suppress compiler warnings on platforms where
|
| + * mutexed_compare_and_swap is not otherwise used. */
|
| + void *pv = &globalMutex;
|
| + mutexed_compare_and_swap(&pv, NULL, NULL);
|
| + usrMutexCleanup();
|
| +
|
| + pIncFn = NULL;
|
| + pDecFn = NULL;
|
| + gIncDecContext = NULL;
|
| +
|
| + return TRUE;
|
| +}
|
|
|
| Property changes on: icu51/source/common/umutex.cpp
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|