9dfc6c4518
X-SVN-Rev: 30431
622 lines
19 KiB
C
622 lines
19 KiB
C
/*
|
|
******************************************************************************
|
|
*
|
|
* 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;
|
|
}
|
|
|
|
|