From aedb354246e3c3172545d25771e1f89ea6425bd9 Mon Sep 17 00:00:00 2001 From: Andy Heninger Date: Fri, 8 Aug 2003 16:23:38 +0000 Subject: [PATCH] ICU-3156 u_setMutexFunctions(), cleanup to mutex implementation X-SVN-Rev: 12787 --- icu4c/source/common/cmemory.c | 4 +- icu4c/source/common/ucln_cmn.c | 5 +- icu4c/source/common/ucln_cmn.h | 2 + icu4c/source/common/umutex.c | 297 ++++++++++-------------- icu4c/source/common/umutex.h | 25 +- icu4c/source/common/unicode/uclean.h | 24 +- icu4c/source/common/usprep.cpp | 5 + icu4c/source/test/cintltst/calltest.c | 2 - icu4c/source/test/cintltst/cintltst.c | 28 --- icu4c/source/test/cintltst/hpmufn.c | 153 +++++++++++- icu4c/source/test/intltest/Makefile.in | 2 +- icu4c/source/test/intltest/intltest.cpp | 14 -- icu4c/source/test/intltest/intltest.dsp | 8 - icu4c/source/test/intltest/itutil.cpp | 8 +- icu4c/source/test/intltest/tsmutex.cpp | 33 --- 15 files changed, 309 insertions(+), 301 deletions(-) diff --git a/icu4c/source/common/cmemory.c b/icu4c/source/common/cmemory.c index a926130e80d..5e914864474 100644 --- a/icu4c/source/common/cmemory.c +++ b/icu4c/source/common/cmemory.c @@ -25,7 +25,7 @@ static const int32_t zeroMem[] = {0, 0, 0, 0, 0, 0}; /* Function Pointers for user-supplied heap functions */ -static void *pContext; +static const void *pContext; static UMemAlloc *pAlloc; static UMemRealloc *pRealloc; static UMemFree *pFree; @@ -81,7 +81,7 @@ uprv_free(void *buffer) { } U_CAPI void U_EXPORT2 -u_setMemoryFunctions(void *context, UMemAlloc *a, UMemRealloc *r, UMemFree *f, UErrorCode *status) +u_setMemoryFunctions(const void *context, UMemAlloc *a, UMemRealloc *r, UMemFree *f, UErrorCode *status) { if (U_FAILURE(*status)) { return; diff --git a/icu4c/source/common/ucln_cmn.c b/icu4c/source/common/ucln_cmn.c index 4edbf93080b..61c9986fe29 100644 --- a/icu4c/source/common/ucln_cmn.c +++ b/icu4c/source/common/ucln_cmn.c @@ -89,7 +89,7 @@ u_cleanup(void) * time. The global mutex is being destroyed so that heap and * resource checkers don't complain. [grhoten] */ - umtx_destroy(NULL); + umtx_cleanup(); cmemory_cleanup(); /* undo any heap functions set by u_setMemoryFunctions(). */ } @@ -163,6 +163,9 @@ U_CFUNC UBool u_ICUStaticInitFunc() * * Do NOT call cmemory_cleanup(). We don't want to cancel the effect of * any u_setHeapFunctions(). + * + * Similarly, do not call umtx_cleanup(); we need to keep any user-set + * mutex callback functions. */ U_CFUNC void u_ICUStaticUnInitFunc() { ucnv_cleanup(); diff --git a/icu4c/source/common/ucln_cmn.h b/icu4c/source/common/ucln_cmn.h index f5c740a7167..72243961054 100644 --- a/icu4c/source/common/ucln_cmn.h +++ b/icu4c/source/common/ucln_cmn.h @@ -53,6 +53,7 @@ U_CFUNC UBool service_cleanup(void); U_CFUNC UBool cmemory_cleanup(void); +U_CFUNC UBool umtx_cleanup(void); /* Only mutexes should be initialized in these functions. */ @@ -60,6 +61,7 @@ U_CFUNC void ucnv_init(UErrorCode *status); U_CFUNC void ures_init(UErrorCode *status); + /* Static Initialization and un-initializatin functions. */ U_CFUNC UBool u_ICUStaticInitFunc(); U_CFUNC void u_ICUStaticUnInitFunc(); diff --git a/icu4c/source/common/umutex.c b/icu4c/source/common/umutex.c index dc5b3163934..e122690aafe 100644 --- a/icu4c/source/common/umutex.c +++ b/icu4c/source/common/umutex.c @@ -39,22 +39,8 @@ #if defined(POSIX) && (ICU_USE_THREADS==1) - /* Usage: uncomment the following, and breakpoint WeAreDeadlocked to - find reentrant issues. */ -/* # define POSIX_DEBUG_REENTRANCY 1 */ # include /* 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 @@ -70,26 +56,13 @@ #include "umutex.h" #include "cmemory.h" -#if (ICU_USE_THREADS == 1) +/* the global mutexes. */ +static UMTX gGlobalMutex = NULL; /* The global ICU mutex */ +static UMTX gIncDecMutex = NULL; /* mutex for atomic inc/dec, for platforms */ + /* that can't do those ops directly. */ -/* the global mutex. */ -static UMTX gGlobalMutex = NULL; - -/* Detect Recursive entries. For debugging only. */ -# ifdef _DEBUG +/* Detect Recursive locking of the global mutex. For debugging only. */ static int32_t gRecursionCount = 0; -# endif - -/* Declare the predefined mutexes. */ -#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 */ - -#endif -#endif /* ICU_USE_THREADS==1 */ /* @@ -101,29 +74,7 @@ static UMtxInit *pMutexInit = NULL; static UMtxDestroy *pMutexDestroy = NULL; static UMtxLock *pMutexLock = NULL; static UMtxUnlock *pMutexUnlock = NULL; -static void *gMutexContext = NULL; - -/* - * umtx_isInitialized - */ -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 */ -} +static const void *gMutexContext = NULL; @@ -133,7 +84,6 @@ umtx_isInitialized(UMTX *mutex) U_CAPI void U_EXPORT2 umtx_lock(UMTX *mutex) { -#if (ICU_USE_THREADS == 1) if (mutex == NULL) { mutex = &gGlobalMutex; @@ -141,50 +91,33 @@ umtx_lock(UMTX *mutex) 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! */ - } + /* 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. */ + 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 */ } if (pMutexLock != NULL) { (*pMutexLock)(gMutexContext, mutex); } else { +#if (ICU_USE_THREADS == 1) #if defined(WIN32) EnterCriticalSection((CRITICAL_SECTION*) *mutex); -#ifdef _DEBUG - if (mutex == &gGlobalMutex) { - gRecursionCount++; - U_ASSERT(gRecursionCount == 1); +#elif defined(POSIX) + pthread_mutex_lock((pthread_mutex_t*) *mutex); +#endif /* cascade of platforms */ +#endif /* ICU_USE_THREADS==1 */ + } + +#if defined(WIN32) && defined(_DEBUG) + 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 /*_DEBUG*/ - -#elif defined(POSIX) -# 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; - } -# endif /* POSIX_DEBUG_REENTRANCY */ -#endif /* cascade of platforms */ - } -#endif /* ICU_USE_THREADS==1 */ } @@ -195,7 +128,6 @@ umtx_lock(UMTX *mutex) U_CAPI void U_EXPORT2 umtx_unlock(UMTX* mutex) { -#if (ICU_USE_THREADS==1) if(mutex == NULL) { mutex = &gGlobalMutex; @@ -203,33 +135,28 @@ umtx_unlock(UMTX* mutex) if(*mutex == NULL) { - return; /* jitterbug 135, fix for multiprocessor machines */ + U_ASSERT(FALSE); /* This mutex is not initialized. */ + return; } +#if defined (WIN32) && defined (_DEBUG) + if (mutex == &gGlobalMutex) { + gRecursionCount--; + U_ASSERT(gRecursionCount == 0); /* Detect unlock of an already unlocked mutex */ + } +#endif + if (pMutexUnlock) { (*pMutexUnlock)(gMutexContext, mutex); } else { +#if (ICU_USE_THREADS==1) #if defined (WIN32) - #ifdef _DEBUG - if (mutex == &gGlobalMutex) { - gRecursionCount--; - U_ASSERT(gRecursionCount == 0); - } - #endif /*_DEBUG*/ - LeaveCriticalSection((CRITICAL_SECTION*)*mutex); - + LeaveCriticalSection((CRITICAL_SECTION*)*mutex); #elif defined (POSIX) - pthread_mutex_unlock((pthread_mutex_t*)*mutex); - -#ifdef POSIX_DEBUG_REENTRANCY - if (mutex == &gGlobalMutex) { - gInMutex = FALSE; - } -#endif - + pthread_mutex_unlock((pthread_mutex_t*)*mutex); #endif /* cascade of platforms */ - } #endif /* ICU_USE_THREADS == 1 */ + } } @@ -237,73 +164,70 @@ umtx_unlock(UMTX* mutex) /* * umtx_raw_init Do the platform specific mutex allocation and initialization */ -#if (ICU_USE_THREADS == 1) -static UMTX umtx_raw_init(void *mem) { +static void umtx_raw_init(UMTX *mutex) { if (pMutexInit != NULL) { UErrorCode status = U_ZERO_ERROR; - if (mem == NULL) { - mem = uprv_malloc(sizeof(UMTX)); - if (mem == NULL) {return NULL;} - } - (*pMutexInit)(gMutexContext, (UMTX *)mem, &status); + (*pMutexInit)(gMutexContext, mutex, &status); if (U_FAILURE(status)) { /* TODO: how should errors here be handled? */ - uprv_free(mem); - return NULL; + return; } } else { +#if (ICU_USE_THREADS == 1) #if defined (WIN32) - if (mem == NULL) { - mem = uprv_malloc(sizeof(CRITICAL_SECTION)); - if (mem == NULL) {return NULL;} + CRITICAL_SECTION *cs = uprv_malloc(sizeof(CRITICAL_SECTION)); + if (cs == NULL) { + return; } - InitializeCriticalSection((CRITICAL_SECTION*)mem); + InitializeCriticalSection(cs); + *mutex = cs; #elif defined( POSIX ) - if (mem == NULL) { - /* TODO: think about eliminating this malloc. */ - mem = uprv_malloc(sizeof(pthread_mutex_t)); - if (mem == NULL) {return NULL;} + pthread_mutex_t *m = uprv_malloc(sizeof(pthread_mutex_t)); + if (m == NULL) { + return; } # if defined (HPUX_CMA) - pthread_mutex_init((pthread_mutex_t*)mem, pthread_mutexattr_default); + pthread_mutex_init(m, pthread_mutexattr_default); # else - pthread_mutex_init((pthread_mutex_t*)mem, NULL); + pthread_mutex_init(m, NULL); # endif + *mutex = m; #endif /* cascade of platforms */ +#else /* ICU_USE_THREADS */ + *mutex = mutex; /* 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 */ } - - return (UMTX *)mem; } -#endif /* ICU_USE_THREADS */ + U_CAPI void U_EXPORT2 umtx_init(UMTX *mutex) { -#if (ICU_USE_THREADS == 1) + if (mutex == NULL) { + mutex = &gGlobalMutex; + } - if (mutex == NULL) /* initialize the global mutex */ - { - /* Note: The initialization of the global mutex is NOT thread safe. */ - if (gGlobalMutex != NULL) { + if (mutex == &gGlobalMutex) { + /* Initialization of the global mutex. */ + if (*mutex != NULL) { + /* Global mutex is already initialized. Nothing more required */ return; } - gGlobalMutex = umtx_raw_init(&gPlatformMutex); - - # ifdef POSIX_DEBUG_REENTRANCY - gInMutex = FALSE; - # endif - #ifdef _DEBUG - gRecursionCount = 0; - #endif + umtx_raw_init(mutex); + gRecursionCount = 0; + /* Initialize the inc/dec mutex, if needed, at the same time as the global ICU mutex */ #ifdef POSIX umtx_raw_init(&gIncDecMutex); #endif } else { - /* Not the global mutex. + /* The mutex to initialize is not the global mutex. * Thread safe initialization, using the global mutex. */ UBool isInitialized; @@ -316,7 +240,7 @@ umtx_init(UMTX *mutex) return; } - tMutex = umtx_raw_init(NULL); + umtx_raw_init(&tMutex); umtx_lock(NULL); if (*mutex == NULL) { @@ -329,45 +253,60 @@ umtx_init(UMTX *mutex) umtx_destroy(&tMutex); } } -#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 */ { mutex = &gGlobalMutex; } - + if (*mutex == NULL) /* someone already did it. */ return; - + + if (pMutexDestroy != NULL) { + (*pMutexDestroy)(gMutexContext, mutex); + } else { +#if (ICU_USE_THREADS == 1) #if defined (WIN32) - DeleteCriticalSection((CRITICAL_SECTION*)*mutex); - + DeleteCriticalSection((CRITICAL_SECTION*)*mutex); + #elif defined (POSIX) - pthread_mutex_destroy((pthread_mutex_t*)*mutex); - + pthread_mutex_destroy((pthread_mutex_t*)*mutex); + #endif - - if (*mutex != gGlobalMutex) - { +#endif /* ICU_USE_THREADS==1 */ + uprv_free(*mutex); } - *mutex = NULL; -#endif /* ICU_USE_THREADS==1 */ + +#if defined (POSIX) + if (mutex == &gGlobalMutex) { + umtx_destroy(&gIncDecMutex); + } +#endif /* POSIX */ } U_CAPI void U_EXPORT2 -u_setMutexFunctions(void *context, UMtxInit *i, UMtxDestroy *d, UMtxLock *l, UMtxUnlock *u, +u_setMutexFunctions(const void *context, UMtxInit *i, UMtxDestroy *d, UMtxLock *l, UMtxUnlock *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; @@ -391,17 +330,36 @@ u_setMutexFunctions(void *context, UMtxInit *i, UMtxDestroy *d, UMtxLock *l, UMt /* * Re-do the equivalent of ICU's static initialization, - * which will recreate the default, resource bundle and converter mutexes + * which will recreate the default, resource bundle and converter mutexes. + * TODO: remove this when remove all ICU static init. */ u_ICUStaticInitFunc(); } - /* + * Mutex Cleanup Function + * + * Destroy the global mutex, and reset the mutex function callback pointers. + */ +U_CFUNC UBool umtx_cleanup(void) { + umtx_destroy(NULL); + pMutexInit = NULL; + pMutexDestroy = NULL; + pMutexLock = NULL; + pMutexUnlock = NULL; + gMutexContext = NULL; + return TRUE; +} + + + +/*----------------------------------------------------------------- + * * umtx_atomic_inc * umtx_atomic_dec - */ + * + *----------------------------------------------------------------*/ #if (ICU_USE_THREADS == 1) #if defined (WIN32) @@ -424,8 +382,6 @@ umtx_atomic_dec(int32_t *p) /* * 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 @@ -433,9 +389,10 @@ umtx_atomic_inc(int32_t *p) { int32_t retVal; - pthread_mutex_lock(&gIncDecMutex); + pthread_mutex_t *m = (pthread_mutex_t*) gIncDecMutex; + pthread_mutex_lock(m); retVal = ++(*p); - pthread_mutex_unlock(&gIncDecMutex); + pthread_mutex_unlock(m); return retVal; } @@ -445,13 +402,15 @@ umtx_atomic_dec(int32_t *p) { int32_t retVal; - pthread_mutex_lock(&gIncDecMutex); + pthread_mutex_t *m = (pthread_mutex_t*) gIncDecMutex; + pthread_mutex_lock(m); retVal = --(*p); - pthread_mutex_unlock(&gIncDecMutex); + pthread_mutex_unlock(m); return retVal; } +/* TODO: Some POSIXy platforms have atomic inc/dec functions available. Use them. */ #else /* No recognized platform. */ diff --git a/icu4c/source/common/umutex.h b/icu4c/source/common/umutex.h index 8908cb8e3ea..a2856769400 100644 --- a/icu4c/source/common/umutex.h +++ b/icu4c/source/common/umutex.h @@ -66,29 +66,24 @@ U_CAPI void U_EXPORT2 umtx_unlock ( UMTX* mutex ); /* Initialize a mutex. Use it this way: umtx_init( &aMutex ); - * ICU Mutexes, aside from the global mutex, must be explicitly initialized - * before use. + * ICU Mutexes must be explicitly initialized before use. + * Initialization of an already initialized mutex has no effect, and is safe to do. + * Initialization of mutexes (other than the global mutex) is thread safe. Two + * threads can concurrently attempt to init the same mutex without causing problems. * @param mutex The given mutex to be initialized */ U_CAPI void U_EXPORT2 umtx_init ( UMTX* mutex ); /* Destroy a mutex. This will free the resources of a mutex. - Use it this way: - umtx_destroy( &aMutex ); - * @param mutex The given mutex to be destroyed + * Use it this way: + * umtx_destroy( &aMutex ); + * Destroying an already destroyed mutex has no effect, and causes no problems. + * This function is not thread safe. Two threads must not attempt to concurrently + * destroy the same mutex. + * @param mutex The given mutex to be destroyed. */ U_CAPI void U_EXPORT2 umtx_destroy( UMTX *mutex ); -/* Test whether an ICU mutex is initialized. - * This function is intended for use from test programs only, - * there should be no need for it from normal code. - * If there is any question about whether a mutex has been initialized, - * simply initialize it again with umtx_init(), which will have - * no effect if the mutex is already initialized. - * @param mutex The given mutex to be tested -*/ -U_CAPI UBool U_EXPORT2 umtx_isInitialized( UMTX *mutex ); - /* diff --git a/icu4c/source/common/unicode/uclean.h b/icu4c/source/common/unicode/uclean.h index 3b7b5d79455..dbd570ba8f5 100644 --- a/icu4c/source/common/unicode/uclean.h +++ b/icu4c/source/common/unicode/uclean.h @@ -98,30 +98,30 @@ u_cleanup(void); */ typedef void *UMTX; -typedef void U_CALLCONV UMtxInit (void *context, UMTX *mutex, UErrorCode*pError); -typedef void U_CALLCONV UMtxDestroy(void *context, UMTX *mutex); -typedef void U_CALLCONV UMtxLock (void *context, UMTX *mutex); -typedef void U_CALLCONV UMtxUnlock (void *context, UMTX *mutex); +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); U_CAPI void U_EXPORT2 -u_setMutexFunctions(void *context, UMtxInit *i, UMtxDestroy *d, UMtxLock *l, UMtxUnlock *u, +u_setMutexFunctions(const void *context, UMtxInit *i, UMtxDestroy *d, UMtxLock *l, UMtxUnlock *u, UErrorCode *status); -typedef void U_CALLCONV UMtxAtomicInc (void *context, UMTX *mutex); -typedef void U_CALLCONV UMtxAtomicDec (void *context, UMTX *mutex); +typedef void U_CALLCONV UMtxAtomicInc (const void *context, UMTX mutex); +typedef void U_CALLCONV UMtxAtomicDec (const void *context, UMTX mutex); U_CAPI void U_EXPORT2 -u_setAtomicIncDecFunctions(void *context, UMtxAtomicInc *inc, UMtxAtomicDec *dec, +u_setAtomicIncDecFunctions(const void *context, UMtxAtomicInc *inc, UMtxAtomicDec *dec, UErrorCode *status); -typedef void *U_CALLCONV UMemAlloc (void *context, size_t size); -typedef void *U_CALLCONV UMemRealloc(void *context, void *mem, size_t size); -typedef void U_CALLCONV UMemFree (void *context, void *mem); +typedef void *U_CALLCONV UMemAlloc (const void *context, size_t size); +typedef void *U_CALLCONV UMemRealloc(const void *context, void *mem, size_t size); +typedef void U_CALLCONV UMemFree (const void *context, void *mem); U_CAPI void U_EXPORT2 -u_setMemoryFunctions(void *context, UMemAlloc *a, UMemRealloc *r, UMemFree *f, +u_setMemoryFunctions(const void *context, UMemAlloc *a, UMemRealloc *r, UMemFree *f, UErrorCode *status); #endif diff --git a/icu4c/source/common/usprep.cpp b/icu4c/source/common/usprep.cpp index d30758710a9..b19a59bf5be 100644 --- a/icu4c/source/common/usprep.cpp +++ b/icu4c/source/common/usprep.cpp @@ -298,6 +298,11 @@ usprep_open(const char* path, if(status == NULL || U_FAILURE(*status)){ return NULL; } + + usprep_init(status); + if (U_FAILURE(*status)) { + return NULL; + } /* initialize the profile struct members */ return usprep_getProfile(path,name,status);; diff --git a/icu4c/source/test/cintltst/calltest.c b/icu4c/source/test/cintltst/calltest.c index 215c6e99798..c9c48d9123b 100644 --- a/icu4c/source/test/cintltst/calltest.c +++ b/icu4c/source/test/cintltst/calltest.c @@ -16,7 +16,6 @@ #include "cintltst.h" -void addSetup(TestNode** root); void addUtility(TestNode** root); void addBreakIter(TestNode** root); void addStandardNamesTest(TestNode **root); @@ -39,7 +38,6 @@ void addHeapMutexTest(TestNode **root); void addAllTests(TestNode** root) { - addSetup(root); /* Leave this test first! */ addUDataTest(root); addPUtilTest(root); addUTF16Test(root); diff --git a/icu4c/source/test/cintltst/cintltst.c b/icu4c/source/test/cintltst/cintltst.c index e2abc3216f3..296e96e9515 100644 --- a/icu4c/source/test/cintltst/cintltst.c +++ b/icu4c/source/test/cintltst/cintltst.c @@ -47,21 +47,7 @@ static char* _testDataPath=NULL; */ void ctest_setICU_DATA(void); -static UBool gMutexInitialized = FALSE; -static void TestMutex(void) { - if (!gMutexInitialized) { - log_verbose("*** Failure! The global mutex was not initialized.\n" - "*** Make sure the right linker was used.\n"); - } -} - -U_CFUNC void addSetup(TestNode** root); - -void addSetup(TestNode** root) -{ - addTest(root, &TestMutex, "setup/TestMutex"); -} #if UCONFIG_NO_LEGACY_CONVERSION # define TRY_CNV_1 "iso-8859-1" @@ -84,9 +70,6 @@ int main(int argc, const char* const argv[]) UResourceBundle *rb; UConverter *cnv; - /* This must be tested before using anything! */ - gMutexInitialized = umtx_isInitialized(NULL); - /* Checkargs */ for(i=1;i -#include static void TestHeapFunctions(void); +static void TestMutexFunctions(void); void addHeapMutexTest(TestNode **root); @@ -28,7 +27,7 @@ void addHeapMutexTest(TestNode** root) { addTest(root, &TestHeapFunctions, "tsutil/TestHeapFunctions" ); - + addTest(root, &TestMutexFunctions, "tsutil/TestMutexFunctions"); } @@ -37,6 +36,12 @@ if (status != expected) { \ log_err("FAIL at %s:%d. Actual status = \"%s\"; Expected status = \"%s\"\n", \ __FILE__, __LINE__, u_errorName(status), u_errorName(expected)); } + +#define TEST_ASSERT(expr) \ +if (!(expr)) { \ + log_err("FAILED Assertion \"" #expr "\" at %s:%d.\n", __FILE__, __LINE__); \ +} + /* * Test Heap Functions. * Implemented on top of the standard malloc heap. @@ -46,9 +51,9 @@ __FILE__, __LINE__, u_errorName(status), u_errorName(expected)); } * Allocations are counted, to check that ICU actually does call back to us. */ int gBlockCount = 0; -void *gContext; +const void *gContext; -void *myMemAlloc(void *context, size_t size) { +void *myMemAlloc(const void *context, size_t size) { char *retPtr = (char *)malloc(size+8); if (retPtr != NULL) { retPtr += 8; @@ -57,7 +62,7 @@ void *myMemAlloc(void *context, size_t size) { return retPtr; } -void myMemFree(void *context, void *mem) { +void myMemFree(const void *context, void *mem) { char *freePtr = (char *)mem; if (freePtr != NULL) { freePtr -= 8; @@ -67,7 +72,7 @@ void myMemFree(void *context, void *mem) { -void *myMemRealloc(void *context, void *mem, size_t size) { +void *myMemRealloc(const void *context, void *mem, size_t size) { char *p = (char *)mem; char *retPtr; @@ -145,7 +150,137 @@ static void TestHeapFunctions() { log_err("Heap functions did not reset after u_cleanup.\n"); } ures_close(rb); - - +} + + +/* + * Test u_setMutexFunctions() + */ + +int gTotalMutexesInitialized = 0; /* Total number of mutexes created */ +int gTotalMutexesActive = 0; /* Total mutexes created, but not destroyed */ +int gAccumulatedLocks = 0; +const void *gMutexContext; + +typedef struct DummyMutex { + int fLockCount; + int fMagic; +} DummyMutex; + + +void myMutexInit(const void *context, UMTX *mutex, UErrorCode *status) { + DummyMutex *theMutex; + + TEST_STATUS(*status, U_ZERO_ERROR); + theMutex = (DummyMutex *)malloc(sizeof(DummyMutex)); + theMutex->fLockCount = 0; + theMutex->fMagic = 123456; + gTotalMutexesInitialized++; + gTotalMutexesActive++; + gMutexContext = context; + *mutex = theMutex; +} + + +void myMutexDestroy(const void *context, UMTX *mutex) { + DummyMutex *This = *(DummyMutex **)mutex; + + gTotalMutexesActive--; + TEST_ASSERT(This->fLockCount == 0); + TEST_ASSERT(This->fMagic == 123456); + This->fMagic = 0; + This->fLockCount = 0; + free(This); +} + +void myMutexLock(const void *context, UMTX *mutex) { + DummyMutex *This = *(DummyMutex **)mutex; + + TEST_ASSERT(This->fMagic == 123456); + This->fLockCount++; + gAccumulatedLocks++; +} + +void myMutexUnlock(const void *context, UMTX *mutex) { + DummyMutex *This = *(DummyMutex **)mutex; + + TEST_ASSERT(This->fMagic == 123456); + This->fLockCount--; + TEST_ASSERT(This->fLockCount >= 0); +} + + + +static void TestMutexFunctions() { + UErrorCode status = U_ZERO_ERROR; + UResourceBundle *rb = NULL; + + + /* Can not set mutex functions if ICU is already initialized */ + u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); + TEST_STATUS(status, U_INVALID_STATE_ERROR); + + /* Un-initialize ICU */ + u_cleanup(); + + /* Can not set Mutex functions with NULL values */ + status = U_ZERO_ERROR; + u_setMutexFunctions(&gContext, NULL, myMutexDestroy, myMutexLock, myMutexUnlock, &status); + TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); + status = U_ZERO_ERROR; + u_setMutexFunctions(&gContext, myMutexInit, NULL, myMutexLock, myMutexUnlock, &status); + TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); + status = U_ZERO_ERROR; + u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, NULL, myMutexUnlock, &status); + TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); + status = U_ZERO_ERROR; + u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, NULL, &status); + TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); + + /* u_setMutexFunctions() should work with null or non-null context pointer */ + status = U_ZERO_ERROR; + u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); + TEST_STATUS(status, U_ZERO_ERROR); + u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); + TEST_STATUS(status, U_ZERO_ERROR); + + + /* After reinitializing ICU, we should not be able to set the mutex funcs again. */ + status = U_ZERO_ERROR; + u_init(&status); + TEST_STATUS(status, U_ZERO_ERROR); + u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); + TEST_STATUS(status, U_INVALID_STATE_ERROR); + + /* Doing ICU operations should cause allocations to come through our test mutexes */ + gBlockCount = 0; + status = U_ZERO_ERROR; + rb = ures_open(NULL, "es", &status); + TEST_STATUS(status, U_ZERO_ERROR); + TEST_ASSERT(gTotalMutexesInitialized > 0); + TEST_ASSERT(gTotalMutexesActive > 0); + + ures_close(rb); + + /* Cleanup should destroy all of the mutexes. */ + u_cleanup(); + status = U_ZERO_ERROR; + TEST_ASSERT(gTotalMutexesInitialized > 0); + TEST_ASSERT(gTotalMutexesActive == 0); + + + /* Additional ICU operations should no longer use our dummy test mutexes */ + gTotalMutexesInitialized = 0; + gTotalMutexesActive = 0; + u_init(&status); + TEST_STATUS(status, U_ZERO_ERROR); + + status = U_ZERO_ERROR; + rb = ures_open(NULL, "fr", &status); + TEST_STATUS(status, U_ZERO_ERROR); + TEST_ASSERT(gTotalMutexesInitialized == 0); + TEST_ASSERT(gTotalMutexesActive == 0); + + ures_close(rb); } diff --git a/icu4c/source/test/intltest/Makefile.in b/icu4c/source/test/intltest/Makefile.in index 705aabd4bb2..0321da2aec1 100644 --- a/icu4c/source/test/intltest/Makefile.in +++ b/icu4c/source/test/intltest/Makefile.in @@ -34,7 +34,7 @@ itercoll.o itformat.o itmajor.o itutil.o jacoll.o lcukocol.o \ loctest.o miscdtfm.o mnkytst.o msfmrgts.o nmfmapts.o nmfmtrt.o \ numfmtst.o numrgts.o pptest.o regcoll.o restest.o restsnew.o sdtfmtts.o svccoll.o tchcfmt.o \ tfsmalls.o tmsgfmt.o trcoll.o tscoll.o tsdate.o tsdcfmsy.o tsdtfmsy.o \ -tsmthred.o tsmutex.o tsnmfmt.o tsputil.o tstnrapi.o tstnorm.o tzbdtest.o \ +tsmthred.o tsnmfmt.o tsputil.o tstnrapi.o tstnorm.o tzbdtest.o \ tzregts.o tztest.o ucdtest.o usettest.o ustrtest.o strcase.o transtst.o strtest.o thcoll.o \ itrbbi.o rbbiapts.o rbbitst.o ittrans.o transapi.o cpdtrtst.o unhxtrts.o hxuntrts.o \ ufltlgts.o testutil.o transrt.o trnserr.o normconf.o sfwdchit.o \ diff --git a/icu4c/source/test/intltest/intltest.cpp b/icu4c/source/test/intltest/intltest.cpp index a43fb50fed2..481094e9630 100644 --- a/icu4c/source/test/intltest/intltest.cpp +++ b/icu4c/source/test/intltest/intltest.cpp @@ -26,7 +26,6 @@ #include "intltest.h" #include "caltztst.h" #include "itmajor.h" -#include "tsmutex.h" #include "umutex.h" @@ -970,9 +969,6 @@ main(int argc, char* argv[]) UConverter *cnv = NULL; const char *warnOrErr = "Failure"; - /* This must be tested before using anything! */ - MutexTest::gMutexInitialized = umtx_isInitialized(NULL); - #ifdef XP_MAC_CONSOLE argc = ccommand( &argv ); #endif @@ -1186,16 +1182,6 @@ main(int argc, char* argv[]) fprintf(stdout, "--------------------------------------\n"); - if (!MutexTest::gMutexInitialized) { - fprintf(stderr, - "#### WARNING!\n" - " The global mutex was not initialized during C++ static initialization.\n" - " You must explicitly initialize ICU by calling u_init() before using ICU\n" - " in multiple threads. If you are using ICU in a single threaded application,\n" - " use of u_init() is recommended, but it is not required.\n" - "#### WARNING!\n" - ); - } if (execCount <= 0) { fprintf(stdout, "***** Not all called tests actually exist! *****\n"); } diff --git a/icu4c/source/test/intltest/intltest.dsp b/icu4c/source/test/intltest/intltest.dsp index 870d7bf0e49..8beea6d400a 100644 --- a/icu4c/source/test/intltest/intltest.dsp +++ b/icu4c/source/test/intltest/intltest.dsp @@ -396,14 +396,6 @@ SOURCE=.\tsmthred.h # End Source File # Begin Source File -SOURCE=.\tsmutex.cpp -# End Source File -# Begin Source File - -SOURCE=.\tsmutex.h -# End Source File -# Begin Source File - SOURCE=.\tsputil.cpp # End Source File # Begin Source File diff --git a/icu4c/source/test/intltest/itutil.cpp b/icu4c/source/test/intltest/itutil.cpp index 846838271e8..6a81f263a58 100644 --- a/icu4c/source/test/intltest/itutil.cpp +++ b/icu4c/source/test/intltest/itutil.cpp @@ -18,7 +18,6 @@ #include "ucdtest.h" #include "restest.h" #include "restsnew.h" -#include "tsmutex.h" #include "tsmthred.h" #include "tsputil.h" #include "uobjtest.h" @@ -33,12 +32,7 @@ void IntlTestUtilities::runIndexedTest( int32_t index, UBool exec, const char* & if (exec) logln("TestSuite Utilities: "); switch (index) { case 0: - name = "MutexTest"; - if (exec) { - logln("MutexTest---"); logln(""); - MutexTest test; - callTest( test, par ); - } + name = "Reserved"; // Was MutexTest, test retired with removal of ICU static init. break; case 1: diff --git a/icu4c/source/test/intltest/tsmutex.cpp b/icu4c/source/test/intltest/tsmutex.cpp index 0afa96eb784..1bf89dca5e3 100644 --- a/icu4c/source/test/intltest/tsmutex.cpp +++ b/icu4c/source/test/intltest/tsmutex.cpp @@ -42,39 +42,6 @@ static void _myUnlock(MutexPointer p) { (imp->lockCount)--; } #endif -////////////////////////// -// -// The Test Class -// -////////////////////////// -MutexTest::MutexTest() { -} - -MutexTest::~MutexTest() { -} - -void MutexTest::runIndexedTest( int32_t index, UBool exec, - const char* &name, char* /*par*/ ) { - if (exec) logln("TestSuite MutexTest: "); - switch (index) { - case 0: name = "TestMutex"; if (exec) TestMutex(); break; - - default: name = ""; break; //needed to end loop - } -} - -UBool MutexTest::gMutexInitialized = FALSE; - -void MutexTest::TestMutex() { - /* This is tested in intltest.cpp before anything starts. */ - if (!gMutexInitialized) { - logln("*** Failure! The global mutex was not initialized.\n" - "*** Make sure the right linker was used.\n"); - } -} -void MutexTest::TestLock() { -} -