/*
******************************************************************************
*
-* Copyright (C) 1997-2003, International Business Machines
+* Copyright (C) 1997-2004, International Business Machines
* Corporation and others. All Rights Reserved.
*
******************************************************************************
#endif
-/* Check our settings... */
#include "unicode/utypes.h"
#include "uassert.h"
+#include "ucln_cmn.h"
#if defined(POSIX) && (ICU_USE_THREADS==1)
- /* Usage: uncomment the following, and breakpoint WeAreDeadlocked to
- find reentrant issues. */
-/* # define POSIX_DEBUG_REENTRANCY 1 */
# include <pthread.h> /* must be first, so that we get the multithread versions of things. */
-# ifdef POSIX_DEBUG_REENTRANCY
- pthread_t gLastThread;
- UBool gInMutex;
-
- U_EXPORT void WeAreDeadlocked();
-
- void WeAreDeadlocked()
- {
- puts("ARGH!! We're deadlocked.. break on WeAreDeadlocked() next time.");
- }
-# endif /* POSIX_DEBUG_REENTRANCY */
#endif /* POSIX && (ICU_USE_THREADS==1) */
#ifdef WIN32
# define WIN32_LEAN_AND_MEAN
-# define NOGDI
+# define VC_EXTRALEAN
# define NOUSER
# define NOSERVICE
# define NOIME
#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 if two or more
+ * threads were more or less simultaenously the first to use ICU in a process, and
+ * were racing into the mutex initialization code.
+ *
+ * The solution for the global mutex init is platform dependent.
+ * On POSIX systems, C-style init 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
+ * avoid problems with race conditions.
+ *
+ * If an application has overridden the ICU mutex implementation
+ * by calling u_setMutexFunctions(), the user supplied init function must
+ * be safe in the event that multiple threads concurrently attempt to init
+ * the same mutex. The first thread should do the init, and the others should
+ * have no effect.
+ *
+ */
+
+#define MAX_MUTEXES 30
+static UMTX gGlobalMutex = NULL;
+static UMTX gIncDecMutex = NULL;
#if (ICU_USE_THREADS == 1)
+static UBool gMutexPoolInitialized = FALSE;
+static char gMutexesInUse[MAX_MUTEXES];
+
+#if defined(WIN32)
+/*-------------------------------------------------------------
+ *
+ * WINDOWS platform variable declarations
+ *
+ *-------------------------------------------------------------*/
+static CRITICAL_SECTION gMutexes[MAX_MUTEXES];
+static CRITICAL_SECTION gGlobalWinMutex;
+
+
+/* On WIN32 mutexes are reentrant. This makes it difficult to debug
+ * deadlocking problems that show up on POSIXy platforms, where
+ * mutexes deadlock upon reentry. ICU contains checking code for
+ * the global mutex as well as for other mutexes in the pool.
+ *
+ * This is for debugging purposes.
+ *
+ * This has no effect on non-WIN32 platforms, non-DEBUG builds, and
+ * non-ICU_USE_THREADS builds.
+ *
+ * Note: The CRITICAL_SECTION structure already has a RecursionCount
+ * member that can be used for this purpose, but portability to
+ * Win98/NT/2K needs to be tested before use. Works fine on XP.
+ * After portability is confirmed, the built-in RecursionCount can be
+ * used, and the gRecursionCountPool can be removed.
+ *
+ * Note: Non-global mutex checking only happens if there is no custom
+ * pMutexLockFn defined. Use one function, not two (don't use
+ * pMutexLockFn and pMutexUnlockFn) so the increment and decrement of
+ * the recursion count don't get out of sync. Users might set just
+ * one function, e.g., to perform a custom action, followed by a
+ * standard call to EnterCriticalSection.
+ */
+#if defined(U_DEBUG) && (ICU_USE_THREADS==1)
+static int32_t gRecursionCount = 0; /* detect global mutex locking */
+static int32_t gRecursionCountPool[MAX_MUTEXES]; /* ditto for non-global */
+#endif
-/* the global mutex. Use it proudly and wash it often. */
-static UMTX gGlobalMutex = NULL;
-# ifdef _DEBUG
-static int32_t gRecursionCount = 0; /* Detect Recursive entries. For debugging only. */
-# endif
-#if defined(WIN32)
-static CRITICAL_SECTION gPlatformMutex;
-
-#elif defined(POSIX)
-static pthread_mutex_t gPlatformMutex; /* The global ICU mutex */
-static pthread_mutex_t gIncDecMutex; /* For use by atomic inc/dec, on Unixes only */
+#elif defined(POSIX)
+/*-------------------------------------------------------------
+ *
+ * POSIX platform variable declarations
+ *
+ *-------------------------------------------------------------*/
+static pthread_mutex_t gMutexes[MAX_MUTEXES] = {
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER,
+ PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER
+};
+#else
+/*-------------------------------------------------------------
+ *
+ * UNKNOWN platform declarations
+ *
+ *-------------------------------------------------------------*/
+static void *gMutexes[MAX_MUTEXES] = {
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL };
+
+/* Unknown platform. OK so long as ICU_USE_THREAD is not set.
+ Note that user can still set mutex functions at run time,
+ and that the global mutex variable is still needed in that case. */
+#if (ICU_USE_THREADS == 1)
+#error no ICU mutex implementation for this platform
+#endif
#endif
#endif /* ICU_USE_THREADS==1 */
-U_CAPI UBool U_EXPORT2
-umtx_isInitialized(UMTX *mutex)
-{
-#if (ICU_USE_THREADS == 1)
- if (mutex == NULL)
- {
- return (UBool)(gGlobalMutex != NULL);
- } else {
- UBool isInited;
- umtx_lock(NULL);
- isInited = (*mutex != NULL);
- umtx_unlock(NULL);
- return isInited;
- }
-#else
- return TRUE; /* Since we don't use threads, it's considered initialized. */
-#endif /* ICU_USE_THREADS==1 */
-}
+/*
+ * User mutex implementation functions. If non-null, call back to these rather than
+ * directly using the system (Posix or Windows) APIs.
+ * (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)
{
-#if (ICU_USE_THREADS == 1)
- if (mutex == NULL)
- {
+ if (mutex == NULL) {
mutex = &gGlobalMutex;
}
- if (*mutex == NULL)
- {
- /* Lazy init of a non-global mutexes on first lock is NOT safe on processors
- * that reorder memory operations. */
- /* U_ASSERT(FALSE); TODO: Turn this back on */
- if (mutex != &gGlobalMutex) {
- umtx_init(mutex);
- } else {
- umtx_init(NULL); /* initialize the global mutex - only get
- here if C++ static init is NOT working,
- and u_init() hasn't been called.
-
- Not thread-safe if this call is contended! */
- }
+ if (*mutex == NULL) {
+ /* Lock of an uninitialized mutex. Initialize it before proceeding. */
+ umtx_init(mutex);
}
-#if defined(WIN32)
-
- EnterCriticalSection((CRITICAL_SECTION*) *mutex);
- #ifdef _DEBUG
- if (mutex == &gGlobalMutex) {
- gRecursionCount++;
- U_ASSERT(gRecursionCount == 1);
- }
- #endif /*_DEBUG*/
+ if (pMutexLockFn != NULL) {
+ (*pMutexLockFn)(gMutexContext, mutex);
+ } else {
+#if (ICU_USE_THREADS == 1)
+#if defined(WIN32)
+ EnterCriticalSection((CRITICAL_SECTION*) *mutex);
#elif defined(POSIX)
+ pthread_mutex_lock((pthread_mutex_t*) *mutex);
+#endif /* cascade of platforms */
+#endif /* ICU_USE_THREADS==1 */
+ }
-# ifdef POSIX_DEBUG_REENTRANCY
- if (gInMutex == TRUE && mutex == &gGlobalMutex) /* in the mutex -- possible deadlock*/
- if(pthread_equal(gLastThread, pthread_self()))
- WeAreDeadlocked();
-# endif
- pthread_mutex_lock((pthread_mutex_t*) *mutex);
-
-# ifdef POSIX_DEBUG_REENTRANCY
- if (mutex == &gGlobalMutex) {
- gLastThread = pthread_self();
- gInMutex = TRUE;
+#if defined(WIN32) && defined(U_DEBUG) && (ICU_USE_THREADS==1)
+ if (mutex == &gGlobalMutex) { /* Detect Reentrant locking of the global mutex. */
+ gRecursionCount++; /* Recursion causes deadlocks on Unixes. */
+ U_ASSERT(gRecursionCount == 1); /* Detection works on Windows. Debug problems there. */
}
-# endif
-#endif
-#endif /* ICU_USE_THREADS==1 */
+ /* This handles gGlobalMutex too, but only if there is no pMutexLockFn */
+ else if (pMutexLockFn == NULL) { /* see comments above */
+ int i = ((CRITICAL_SECTION*)*mutex) - &gMutexes[0];
+ U_ASSERT(i >= 0 && i < MAX_MUTEXES);
+ ++gRecursionCountPool[i];
+
+ U_ASSERT(gRecursionCountPool[i] == 1); /* !Detect Deadlock! */
+
+ /* This works and is fast, but needs testing on Win98/NT/2K.
+ See comments above. [alan]
+ U_ASSERT((CRITICAL_SECTION*)*mutex >= &gMutexes[0] &&
+ (CRITICAL_SECTION*)*mutex <= &gMutexes[MAX_MUTEXES]);
+ U_ASSERT(((CRITICAL_SECTION*)*mutex)->RecursionCount == 1);
+ */
+ }
+#endif /*U_DEBUG*/
}
+
+
+/*
+ * umtx_unlock
+ */
U_CAPI void U_EXPORT2
umtx_unlock(UMTX* mutex)
{
-#if (ICU_USE_THREADS==1)
- if(mutex == NULL)
- {
+ if(mutex == NULL) {
mutex = &gGlobalMutex;
}
- if(*mutex == NULL)
- {
- return; /* jitterbug 135, fix for multiprocessor machines */
+ if(*mutex == NULL) {
+#if (ICU_USE_THREADS == 1)
+ U_ASSERT(FALSE); /* This mutex is not initialized. */
+#endif
+ return;
}
-#if defined (WIN32)
- #ifdef _DEBUG
+#if defined (WIN32) && defined (U_DEBUG) && (ICU_USE_THREADS==1)
if (mutex == &gGlobalMutex) {
gRecursionCount--;
- U_ASSERT(gRecursionCount == 0);
+ U_ASSERT(gRecursionCount == 0); /* Detect unlock of an already unlocked mutex */
}
- #endif /*_DEBUG*/
- LeaveCriticalSection((CRITICAL_SECTION*)*mutex);
-
-#elif defined (POSIX)
- pthread_mutex_unlock((pthread_mutex_t*)*mutex);
-
-#ifdef POSIX_DEBUG_REENTRANCY
- if (mutex == &gGlobalMutex) {
- gInMutex = FALSE;
+ /* This handles gGlobalMutex too, but only if there is no pMutexLockFn */
+ else if (pMutexLockFn == NULL) { /* see comments above */
+ int i = ((CRITICAL_SECTION*)*mutex) - &gMutexes[0];
+ U_ASSERT(i >= 0 && i < MAX_MUTEXES);
+ --gRecursionCountPool[i];
+
+ U_ASSERT(gRecursionCountPool[i] == 0); /* !Detect Deadlock! */
+
+ /* This works and is fast, but needs testing on Win98/NT/2K.
+ Note that RecursionCount will be 1, not 0, since we haven't
+ left the CRITICAL_SECTION yet. See comments above. [alan]
+ U_ASSERT((CRITICAL_SECTION*)*mutex >= &gMutexes[0] &&
+ (CRITICAL_SECTION*)*mutex <= &gMutexes[MAX_MUTEXES]);
+ U_ASSERT(((CRITICAL_SECTION*)*mutex)->RecursionCount == 1);
+ */
}
#endif
-#endif
+ if (pMutexUnlockFn) {
+ (*pMutexUnlockFn)(gMutexContext, mutex);
+ } else {
+#if (ICU_USE_THREADS==1)
+#if defined (WIN32)
+ LeaveCriticalSection((CRITICAL_SECTION*)*mutex);
+#elif defined (POSIX)
+ pthread_mutex_unlock((pthread_mutex_t*)*mutex);
+#endif /* cascade of platforms */
#endif /* ICU_USE_THREADS == 1 */
+ }
}
+
/*
- * umtx_raw_init Do the platform specific mutex allocation and initialization
+ * initGlobalMutex Do the platform specific initialization of the ICU global mutex.
+ * Separated out from the other mutexes because it is different:
+ * Mutex storage is static for POSIX, init must be thread safe
+ * without the use of another mutex.
*/
+static void initGlobalMutex() {
+ /*
+ * If User Supplied mutex functions are in use
+ * init the icu global mutex using them.
+ */
+ if (pMutexInitFn != NULL) {
+ if (gGlobalMutex==NULL) {
+ UErrorCode status = U_ZERO_ERROR;
+ (*pMutexInitFn)(gMutexContext, &gGlobalMutex, &status);
+ if (U_FAILURE(status)) {
+ /* TODO: how should errors here be handled? */
+ return;
+ }
+ }
+ return;
+ }
+
+ /* No user override of mutex functions.
+ * Use default ICU mutex implementations.
+ */
#if (ICU_USE_THREADS == 1)
-static UMTX umtx_raw_init(void *mem) {
- #if defined (WIN32)
- if (mem == NULL) {
- mem = uprv_malloc(sizeof(CRITICAL_SECTION));
- if (mem == NULL) {return NULL;}
+ /*
+ * for Windows, init the pool of critical sections that we
+ * will use as needed for ICU mutexes.
+ */
+#if defined (WIN32)
+ if (gMutexPoolInitialized == FALSE) {
+ int i;
+ for (i=0; i<MAX_MUTEXES; i++) {
+ InitializeCriticalSection(&gMutexes[i]);
+#if defined (U_DEBUG)
+ gRecursionCountPool[i] = 0; /* see comments above */
+#endif
}
- InitializeCriticalSection((CRITICAL_SECTION*)mem);
- #elif defined( POSIX )
- if (mem == NULL) {
- mem = uprv_malloc(sizeof(pthread_mutex_t));
- if (mem == NULL) {return NULL;}
+ gMutexPoolInitialized = TRUE;
+ }
+#elif defined (POSIX)
+ /* TODO: experimental code. Shouldn't need to explicitly init the mutexes. */
+ if (gMutexPoolInitialized == FALSE) {
+ int i;
+ for (i=0; i<MAX_MUTEXES; i++) {
+ pthread_mutex_init(&gMutexes[i], NULL);
}
- # if defined (HPUX_CMA)
- pthread_mutex_init((pthread_mutex_t*)mem, pthread_mutexattr_default);
- # else
- pthread_mutex_init((pthread_mutex_t*)mem, NULL);
- # endif
- #endif
- return (UMTX *)mem;
+ gMutexPoolInitialized = TRUE;
+ }
+#endif
+
+ /*
+ * for both Windows & POSIX, the first mutex in the array is used
+ * for the ICU global mutex.
+ */
+ gGlobalMutex = &gMutexes[0];
+ gMutexesInUse[0] = 1;
+
+#else /* ICU_USE_THREADS */
+ gGlobalMutex = &gGlobalMutex; /* With no threads, we must still set the mutex to
+ * some non-null value to make the rest of the
+ * (not ifdefed) mutex code think that it is initialized.
+ */
+#endif /* ICU_USE_THREADS */
}
-#endif /* ICU_USE_THREADS */
-U_CAPI void U_EXPORT2
-umtx_init(UMTX *mutex)
-{
-#if (ICU_USE_THREADS == 1)
-
- if (mutex == NULL) /* initialize the global mutex */
- {
- /* Note: The initialization of the global mutex is NOT thread safe. */
- if (gGlobalMutex != NULL) {
- return;
- }
- gGlobalMutex = umtx_raw_init(&gPlatformMutex);
- # ifdef POSIX_DEBUG_REENTRANCY
- gInMutex = FALSE;
- # endif
- #ifdef _DEBUG
- gRecursionCount = 0;
- #endif
- #ifdef POSIX
- umtx_raw_init(&gIncDecMutex);
- #endif
+U_CAPI void U_EXPORT2
+umtx_init(UMTX *mutex)
+{
+ if (mutex == NULL || mutex == &gGlobalMutex) {
+ initGlobalMutex();
} else {
- /* Not the global mutex.
- * Thread safe initialization, using the global mutex.
- */
- UBool isInitialized;
- UMTX tMutex = NULL;
-
umtx_lock(NULL);
- isInitialized = (*mutex != NULL);
- umtx_unlock(NULL);
- if (isInitialized) {
+ if (*mutex != NULL) {
+ /* Another thread initialized this mutex first. */
+ umtx_unlock(NULL);
return;
}
- tMutex = umtx_raw_init(NULL);
-
- umtx_lock(NULL);
- if (*mutex == NULL) {
- *mutex = tMutex;
- tMutex = NULL;
+ if (pMutexInitFn != NULL) {
+ UErrorCode status = U_ZERO_ERROR;
+ (*pMutexInitFn)(gMutexContext, mutex, &status);
+ /* TODO: how to report failure on init? */
+ umtx_unlock(NULL);
+ return;
+ }
+ else {
+#if (ICU_USE_THREADS == 1)
+ /* Search through our pool of pre-allocated mutexes for one that is not
+ * already in use. */
+ int i;
+ for (i=0; i<MAX_MUTEXES; i++) {
+ if (gMutexesInUse[i] == 0) {
+ gMutexesInUse[i] = 1;
+ *mutex = &gMutexes[i];
+ break;
+ }
+ }
+#endif
}
umtx_unlock(NULL);
-
- umtx_destroy(&tMutex); /* NOP if (tmutex == NULL) */
+
+#if (ICU_USE_THREADS == 1)
+ /* No more mutexes were available from our pre-allocated pool. */
+ /* TODO: how best to deal with this? */
+ U_ASSERT(*mutex != NULL);
+#endif
}
-#endif /* ICU_USE_THREADS==1 */
}
+
+/*
+ * 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) {
-#if (ICU_USE_THREADS == 1)
- if (mutex == NULL) /* destroy the global mutex */
- {
+ if (mutex == NULL) { /* destroy the global mutex */
mutex = &gGlobalMutex;
}
-
- if (*mutex == NULL) /* someone already did it. */
+
+ if (*mutex == NULL) { /* someone already did it. */
return;
+ }
-#if defined (WIN32)
- DeleteCriticalSection((CRITICAL_SECTION*)*mutex);
-
-#elif defined (POSIX)
- pthread_mutex_destroy((pthread_mutex_t*)*mutex);
+ /* The life of the inc/dec mutex is tied to that of the global mutex. */
+ if (mutex == &gGlobalMutex) {
+ umtx_destroy(&gIncDecMutex);
+ }
+ if (pMutexDestroyFn != NULL) {
+ /* Mutexes are being managed by the app. Call back to it for the destroy. */
+ (*pMutexDestroyFn)(gMutexContext, mutex);
+ }
+ else {
+#if (ICU_USE_THREADS == 1)
+ /* Return this mutex to the pool of available mutexes, if it came from the
+ * pool in the first place.
+ */
+ /* TODO use pointer math here, instead of iterating! */
+ int i;
+ for (i=0; i<MAX_MUTEXES; i++) {
+ if (*mutex == &gMutexes[i]) {
+ gMutexesInUse[i] = 0;
+ break;
+ }
+ }
#endif
-
- if (*mutex != gGlobalMutex)
- {
- uprv_free(*mutex);
}
*mutex = NULL;
-#endif /* ICU_USE_THREADS==1 */
}
-#if (ICU_USE_THREADS == 1)
+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;
+ }
-/*
- * umtx_atomic_inc
- * umtx_atomic_dec
- */
+ /* 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 defined (WIN32)
-/*
- * Win32 - use the Windows API functions for atomic increment and decrement.
- */
-U_CAPI int32_t U_EXPORT2
-umtx_atomic_inc(int32_t *p)
-{
- return InterlockedIncrement(p);
-}
+ /* If ICU is not in an initial state, disallow this operation. */
+ if (cmemory_inUse()) {
+ *status = U_INVALID_STATE_ERROR;
+ return;
+ }
-U_CAPI int32_t U_EXPORT2
-umtx_atomic_dec(int32_t *p)
-{
- return InterlockedDecrement(p);
+ /* Swap in the mutex function pointers. */
+ pMutexInitFn = i;
+ pMutexDestroyFn = d;
+ pMutexLockFn = l;
+ pMutexUnlockFn = u;
+ gMutexContext = context;
+ gGlobalMutex = NULL; /* For POSIX, the global mutex will be pre-initialized */
+ /* Undo that, force re-initialization when u_init() */
+ /* happens. */
}
-#elif defined (POSIX)
-/*
- * POSIX platforms without specific atomic operations. Use a posix mutex
- * to protect the increment and decrement.
- * The IncDecMutex is in static storage so we don't have to come back and delete it
- * when the process exits.
- */
-U_CAPI int32_t U_EXPORT2
-umtx_atomic_inc(int32_t *p)
-{
- int32_t retVal;
- pthread_mutex_lock(&gIncDecMutex);
- retVal = ++(*p);
- pthread_mutex_unlock(&gIncDecMutex);
- return retVal;
-}
+/*-----------------------------------------------------------------
+ *
+ * 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 void *gIncDecContext = NULL;
U_CAPI int32_t U_EXPORT2
-umtx_atomic_dec(int32_t *p)
-{
- int32_t retVal;
+umtx_atomic_inc(int32_t *p) {
+ int32_t retVal;
+ if (pIncFn) {
+ retVal = (*pIncFn)(gIncDecContext, p);
+ } else {
+ #if defined (WIN32) && ICU_USE_THREADS == 1
+ retVal = InterlockedIncrement((LONG*)p);
+ #elif defined (POSIX) && ICU_USE_THREADS == 1
+ umtx_lock(&gIncDecMutex);
+ retVal = ++(*p);
+ umtx_unlock(&gIncDecMutex);
+ #else
+ /* Unknown Platform, or ICU thread support compiled out. */
+ retVal = ++(*p);
+ #endif
+ }
+ return retVal;
+}
- pthread_mutex_lock(&gIncDecMutex);
- retVal = --(*p);
- pthread_mutex_unlock(&gIncDecMutex);
+U_CAPI int32_t U_EXPORT2
+umtx_atomic_dec(int32_t *p) {
+ int32_t retVal;
+ if (pDecFn) {
+ retVal = (*pDecFn)(gIncDecContext, p);
+ } else {
+ #if defined (WIN32) && ICU_USE_THREADS == 1
+ retVal = InterlockedDecrement((LONG*)p);
+ #elif defined (POSIX) && ICU_USE_THREADS == 1
+ umtx_lock(&gIncDecMutex);
+ retVal = --(*p);
+ umtx_unlock(&gIncDecMutex);
+ #else
+ /* Unknown Platform, or ICU thread support compiled out. */
+ retVal = --(*p);
+ #endif
+ }
return retVal;
}
+/* TODO: Some POSIXy platforms have atomic inc/dec functions available. Use them. */
-#else
-
-/* No recognized platform. */
-#error No atomic increment and decrement defined for this platform. \
- Either use the --disable-threads configure option, or define those functions in this file.
-#endif /* Platform selection for atomic_inc and dec. */
-#else /* (ICU_USE_THREADS == 0) */
-/* Threads disabled here */
+U_CAPI void U_EXPORT2
+u_setAtomicIncDecFunctions(const void *context, UMtxAtomicFn *ip, UMtxAtomicFn *dp,
+ UErrorCode *status) {
+ int32_t testInt;
+ 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;
-U_CAPI int32_t U_EXPORT2
-umtx_atomic_inc(int32_t *p) {
- return ++(*p);
+ 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);
}
-U_CAPI int32_t U_EXPORT2
-umtx_atomic_dec(int32_t *p) {
- return --(*p);
-}
-#endif /* (ICU_USE_THREADS == 1) */
+/*
+ * Mutex Cleanup Function
+ *
+ * Destroy the global mutex(es), and reset the mutex function callback pointers.
+ */
+U_CFUNC UBool umtx_cleanup(void) {
+ umtx_destroy(NULL);
+ pMutexInitFn = NULL;
+ pMutexDestroyFn = NULL;
+ pMutexLockFn = NULL;
+ pMutexUnlockFn = NULL;
+ gMutexContext = NULL;
+ gGlobalMutex = NULL;
+ pIncFn = NULL;
+ pDecFn = NULL;
+ gIncDecMutex = NULL;
+
+#if (ICU_USE_THREADS == 1)
+ if (gMutexPoolInitialized) {
+ int i;
+ for (i=0; i<MAX_MUTEXES; i++) {
+ if (gMutexesInUse[i]) {
+#if defined (WIN32)
+ DeleteCriticalSection(&gMutexes[i]);
+#elif defined (POSIX)
+ pthread_mutex_destroy(&gMutexes[i]);
+#endif
+ gMutexesInUse[i] = 0;
+ }
+ }
+ }
+ gMutexPoolInitialized = FALSE;
+#endif
+ return TRUE;
+}