ICU-3156 u_setIncDecFunctions(), API Docs

X-SVN-Rev: 12799
This commit is contained in:
Andy Heninger 2003-08-08 23:56:02 +00:00
parent e71305bba2
commit c934d47697
3 changed files with 327 additions and 115 deletions

View File

@ -66,34 +66,32 @@ static int32_t gRecursionCount = 0;
/*
* User mutex implementatin functions. If non-null, call back to these rather than
* 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 UMtxInit *pMutexInit = NULL;
static UMtxDestroy *pMutexDestroy = NULL;
static UMtxLock *pMutexLock = NULL;
static UMtxUnlock *pMutexUnlock = NULL;
static const void *gMutexContext = NULL;
static UMtxInit *pMutexInit = NULL;
static UMtxFunc *pMutexDestroy = NULL;
static UMtxFunc *pMutexLock = NULL;
static UMtxFunc *pMutexUnlock = NULL;
static const void *gMutexContext = NULL;
/*
* umtx_lock
* umtx_lock
*/
U_CAPI void U_EXPORT2
umtx_lock(UMTX *mutex)
{
if (mutex == NULL)
{
if (mutex == NULL) {
mutex = &gGlobalMutex;
}
if (*mutex == NULL)
{
if (*mutex == NULL) {
/* Attempt to lock an uninitialized mutex. Not Supported.
* Note that earlier versions of ICU supported lazy mutex initialization.
* That is not thread safe on CPUs that reorder memory operations. */
* That was not thread safe on CPUs that reorder memory operations. */
U_ASSERT(FALSE);
umtx_init(mutex); /* But, in case someone really screwed up, we will
* still do the lazy init to try to avoid a crash */
@ -265,13 +263,13 @@ umtx_init(UMTX *mutex)
*/
U_CAPI void U_EXPORT2
umtx_destroy(UMTX *mutex) {
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 (pMutexDestroy != NULL) {
(*pMutexDestroy)(gMutexContext, mutex);
@ -300,7 +298,7 @@ umtx_destroy(UMTX *mutex) {
U_CAPI void U_EXPORT2
u_setMutexFunctions(const void *context, UMtxInit *i, UMtxDestroy *d, UMtxLock *l, UMtxUnlock *u,
u_setMutexFunctions(const void *context, UMtxInit *i, UMtxFunc *d, UMtxFunc *l, UMtxFunc *u,
UErrorCode *status) {
if (U_FAILURE(*status)) {
return;
@ -337,10 +335,103 @@ u_setMutexFunctions(const void *context, UMtxInit *i, UMtxDestroy *d, UMtxLock *
}
/*-----------------------------------------------------------------
*
* Atomic Increment and Decrement
* umtx_atomic_inc
* umtx_atomic_dec
*
*----------------------------------------------------------------*/
/* Pointers to user-supplied inc/dec functions. Null if not funcs have been set. */
static UMtxAtomicF *pIncF = NULL;
static UMtxAtomicF *pDecF = NULL;
static void *gIncDecContext;
U_CAPI int32_t U_EXPORT2
umtx_atomic_inc(int32_t *p) {
int32_t retVal;
if (pIncF) {
retVal = (*pIncF)(gIncDecContext, p);
} else {
#if defined (WIN32) && ICU_USE_THREADS == 1
retVal = InterlockedIncrement(p);
#elif defined (POSIX) && ICU_USE_THREADS == 1
pthread_mutex_t *m = (pthread_mutex_t*) gIncDecMutex;
pthread_mutex_lock(m);
retVal = ++(*p);
pthread_mutex_unlock(m);
#else
/* Unknown Platform, or ICU thread support compiled out. */
retVal = ++(*p);
#endif
}
return retVal;
}
U_CAPI int32_t U_EXPORT2
umtx_atomic_dec(int32_t *p) {
int32_t retVal;
if (pDecF) {
retVal = (*pDecF)(gIncDecContext, p);
} else {
#if defined (WIN32) && ICU_USE_THREADS == 1
retVal = InterlockedDecrement(p);
#elif defined (POSIX) && ICU_USE_THREADS == 1
pthread_mutex_t *m = (pthread_mutex_t*) gIncDecMutex;
pthread_mutex_lock(m);
retVal = --(*p);
pthread_mutex_unlock(m);
#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. */
U_CAPI void U_EXPORT2
u_setAtomicIncDecFunctions(const void *context, UMtxAtomicF *ip, UMtxAtomicF *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;
}
pIncF = ip;
pDecF = dp;
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);
}
/*
* Mutex Cleanup Function
*
* Destroy the global mutex, and reset the mutex function callback pointers.
* Destroy the global mutex(es), and reset the mutex function callback pointers.
*/
U_CFUNC UBool umtx_cleanup(void) {
umtx_destroy(NULL);
@ -349,93 +440,11 @@ U_CFUNC UBool umtx_cleanup(void) {
pMutexLock = NULL;
pMutexUnlock = NULL;
gMutexContext = NULL;
pIncF = NULL;
pDecF = NULL;
return TRUE;
}
/*-----------------------------------------------------------------
*
* umtx_atomic_inc
* umtx_atomic_dec
*
*----------------------------------------------------------------*/
#if (ICU_USE_THREADS == 1)
#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);
}
U_CAPI int32_t U_EXPORT2
umtx_atomic_dec(int32_t *p)
{
return InterlockedDecrement(p);
}
#elif defined (POSIX)
/*
* POSIX platforms without specific atomic operations. Use a posix mutex
* to protect the increment and decrement.
*/
U_CAPI int32_t U_EXPORT2
umtx_atomic_inc(int32_t *p)
{
int32_t retVal;
pthread_mutex_t *m = (pthread_mutex_t*) gIncDecMutex;
pthread_mutex_lock(m);
retVal = ++(*p);
pthread_mutex_unlock(m);
return retVal;
}
U_CAPI int32_t U_EXPORT2
umtx_atomic_dec(int32_t *p)
{
int32_t retVal;
pthread_mutex_t *m = (pthread_mutex_t*) gIncDecMutex;
pthread_mutex_lock(m);
retVal = --(*p);
pthread_mutex_unlock(m);
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 int32_t U_EXPORT2
umtx_atomic_inc(int32_t *p) {
return ++(*p);
}
U_CAPI int32_t U_EXPORT2
umtx_atomic_dec(int32_t *p) {
return --(*p);
}
#endif /* (ICU_USE_THREADS == 1) */

View File

@ -92,34 +92,142 @@ u_cleanup(void);
/**
* An opaque type that represents an ICU mutex.
* An opaque pointer type that represents an ICU mutex.
* For user-implemented mutexes, the value will typically point to a
* struct or object that implements the mutex.
* @draft ICU 2.8
* @system
*/
typedef void *UMTX;
typedef void U_CALLCONV UMtxInit (const void *context, UMTX *mutex, UErrorCode*pError);
typedef void U_CALLCONV UMtxDestroy(const void *context, UMTX *mutex);
typedef void U_CALLCONV UMtxLock (const void *context, UMTX *mutex);
typedef void U_CALLCONV UMtxUnlock (const void *context, UMTX *mutex);
/**
* Function Pointer type for a user supplied mutex initialization function.
* The user-supplied function will be called by ICU whenever ICU needs to create a
* new mutex. The function implementation should create a mutex, and store a pointer
* to something that uniquely identifies the mutex into the UMTX that is supplied
* as a paramter.
* @param context user supplied value, obtained from from u_setMutexFunctions().
* @param mutex Receives a pointer that identifies the new mutex.
* The mutex init function must set the UMTX to a non-null value.
* Subsequent calls by ICU to lock, unlock, or destroy a mutex will
* identify the mutex by the UMTX value.
* @param status Error status. Report errors back to ICU by setting this variable
* with an error code.
*/
typedef void U_CALLCONV UMtxInit (const void *context, UMTX *mutex, UErrorCode* pError);
/**
* Function Pointer type for a user supplied mutex functions.
* One of the user-supplied functions with this signature will be called by ICU
* whenever ICU needs to lock, unlock, or destroy a mutex.
* @param context user supplied value, obtained from from u_setMutexFunctions().
* @param mutex specify the mutex on which to operate.
* @draft ICU 2.8
* @system
*/
typedef void U_CALLCONV UMtxFunc (const void *context, UMTX *mutex);
/**
* Set the functions that ICU will use for mutex operations
* Use of this function is optional; by default (without this function), ICU will
* directly access system functions for mutex operations
* This function can only be used when ICU is in an initial, unused state, before
* u_init() has been called.
* This function may be used even when ICU has been built without multi-threaded
* support (see ICU_USE_THREADS pre-processor variable)
* @param context This pointer value will be saved, and then (later) passed as
* a parameter to the increment and decrement functions each time they
* are called. This function can only be called
* @param inc Pointer to a function to do an atomic increment operation. Must be non-null.
* @param dec Pointer to a function to do an atomic decrement operation. Must be non-null.
* @param status Receives error values.
* @draft ICU 2.8
* @system
*/
U_CAPI void U_EXPORT2
u_setMutexFunctions(const void *context, UMtxInit *i, UMtxDestroy *d, UMtxLock *l, UMtxUnlock *u,
u_setMutexFunctions(const void *context, UMtxInit *i, UMtxFunc *d, UMtxFunc *l, UMtxFunc *u,
UErrorCode *status);
typedef void U_CALLCONV UMtxAtomicInc (const void *context, UMTX mutex);
typedef void U_CALLCONV UMtxAtomicDec (const void *context, UMTX mutex);
/**
* Pointer type for a user supplied atomic increment or decrement function.
* @param context user supplied value, obtained from from u_setAtomicIncDecFunctions().
* @param p Pointer to a 32 bit int to be incremented or decremented
* @return The value of the variable after the inc or dec operation.
* @draft ICU 2.8
* @system
*/
typedef int32_t U_CALLCONV UMtxAtomicF (const void *context, int32_t *p);
/**
* Set the functions that ICU will use for atomic increment and decrement of int32_t values.
* Use of this function is optional; by default (without this function), ICU will
* use its own internal implementation of atomic increment/decrement.
* This function can only be used when ICU is in an initial, unused state, before
* u_init() has been called.
* @param context This pointer value will be saved, and then (later) passed as
* a parameter to the increment and decrement functions each time they
* are called. This function can only be called
* @param inc Pointer to a function to do an atomic increment operation. Must be non-null.
* @param dec Pointer to a function to do an atomic decrement operation. Must be non-null.
* @param status Receives error values.
* @draft ICU 2.8
* @system
*/
U_CAPI void U_EXPORT2
u_setAtomicIncDecFunctions(const void *context, UMtxAtomicInc *inc, UMtxAtomicDec *dec,
u_setAtomicIncDecFunctions(const void *context, UMtxAtomicF *inc, UMtxAtomicF *dec,
UErrorCode *status);
/**
* Pointer type for a user supplied memory allocation function.
* @param context user supplied value, obtained from from u_setMemoryFunctions().
* @param size The number of bytes to be allocated
* @return Pointer to the newly allocated memory, or NULL if the allocation failed.
* @draft ICU 2.8
* @system
*/
typedef void *U_CALLCONV UMemAlloc (const void *context, size_t size);
/**
* Pointer type for a user supplied memory re-allocation function.
* @param context user supplied value, obtained from from u_setMemoryFunctions().
* @param size The number of bytes to be allocated
* @return Pointer to the newly allocated memory, or NULL if the allocation failed.
* @draft ICU 2.8
* @system
*/
typedef void *U_CALLCONV UMemRealloc(const void *context, void *mem, size_t size);
/**
* Pointer type for a user supplied memory free function. Behavior should be
* similar the standard C library free().
* @param context user supplied value, obtained from from u_setMemoryFunctions().
* @param mem Pointer to the memory block to be resized
* @param size The new size for the block
* @return Pointer to the resized memory block, or NULL if the resizing failed.
* @draft ICU 2.8
* @system
*/
typedef void U_CALLCONV UMemFree (const void *context, void *mem);
/**
* Set the functions that ICU will use for memory allocation.
* Use of this function is optional; by default (without this function), ICU will
* use the standard C library malloc() and free() functions.
* This function can only be used when ICU is in an initial, unused state, before
* u_init() has been called.
* @param context This pointer value will be saved, and then (later) passed as
* a parameter to the memory functions each time they
* are called.
* @param a Pointer to a user-supplied malloc function.
* @param r Pointer to a user-supplied realloc function.
* @param f Pointer to a user-supplied free function.
* @param status Receives error values.
* @draft ICU 2.8
* @system
*/
U_CAPI void U_EXPORT2
u_setMemoryFunctions(const void *context, UMemAlloc *a, UMemRealloc *r, UMemFree *f,
UErrorCode *status);

View File

@ -13,12 +13,13 @@
#include "unicode/uchar.h"
#include "unicode/ures.h"
#include "cintltst.h"
#include "umutex.h"
#include <stdlib.h>
static void TestHeapFunctions(void);
static void TestMutexFunctions(void);
static void TestIncDecFunctions(void);
void addHeapMutexTest(TestNode **root);
@ -26,8 +27,9 @@ void addHeapMutexTest(TestNode **root);
void
addHeapMutexTest(TestNode** root)
{
addTest(root, &TestHeapFunctions, "tsutil/TestHeapFunctions" );
addTest(root, &TestMutexFunctions, "tsutil/TestMutexFunctions");
addTest(root, &TestHeapFunctions, "tsutil/TestHeapFunctions" );
addTest(root, &TestMutexFunctions, "tsutil/TestMutexFunctions" );
addTest(root, &TestIncDecFunctions, "tsutil/TestIncDecFunctions");
}
@ -284,3 +286,96 @@ static void TestMutexFunctions() {
ures_close(rb);
}
/*
* Test Atomic Increment & Decrement Functions
*/
int gIncCount = 0;
int gDecCount = 0;
const void *gIncDecContext;
static int32_t myIncFunc(const void *context, int32_t *p) {
int32_t retVal;
TEST_ASSERT(context == gIncDecContext);
gIncCount++;
retVal = ++(*p);
return retVal;
}
static int32_t myDecFunc(const void *context, int32_t *p) {
int32_t retVal;
TEST_ASSERT(context == gIncDecContext);
gDecCount++;
retVal = --(*p);
return retVal;
}
static void TestIncDecFunctions() {
UErrorCode status = U_ZERO_ERROR;
int32_t t;
/* Can not set mutex functions if ICU is already initialized */
u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status);
TEST_STATUS(status, U_INVALID_STATE_ERROR);
/* Un-initialize ICU */
u_cleanup();
/* Can not set functions with NULL values */
status = U_ZERO_ERROR;
u_setAtomicIncDecFunctions(&gIncDecContext, NULL, myDecFunc, &status);
TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
status = U_ZERO_ERROR;
u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, NULL, &status);
TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
/* u_setIncDecFunctions() should work with null or non-null context pointer */
status = U_ZERO_ERROR;
u_setAtomicIncDecFunctions(NULL, myIncFunc, myDecFunc, &status);
TEST_STATUS(status, U_ZERO_ERROR);
u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status);
TEST_STATUS(status, U_ZERO_ERROR);
/* After reinitializing ICU, we should not be able to set the inc/dec funcs again. */
status = U_ZERO_ERROR;
u_init(&status);
TEST_STATUS(status, U_ZERO_ERROR);
u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status);
TEST_STATUS(status, U_INVALID_STATE_ERROR);
/* Doing ICU operations should cause our functions to be called */
gIncCount = 0;
gDecCount = 0;
umtx_atomic_inc(&t);
umtx_atomic_dec(&t);
TEST_ASSERT(gIncCount > 0);
TEST_ASSERT(gDecCount > 0);
/* Cleanup should cancel use of our inc/dec functions. */
/* Additional ICU operations should not use them */
u_cleanup();
gIncCount = 0;
gDecCount = 0;
status = U_ZERO_ERROR;
u_init(&status);
TEST_ASSERT(gIncCount == 0);
TEST_ASSERT(gDecCount == 0);
status = U_ZERO_ERROR;
umtx_atomic_inc(&t);
umtx_atomic_dec(&t);
TEST_STATUS(status, U_ZERO_ERROR);
TEST_ASSERT(gIncCount == 0);
TEST_ASSERT(gDecCount == 0);
}