diff --git a/icu4c/source/common/ucnv.c b/icu4c/source/common/ucnv.c index 445d0a9f517..21bb020f0d2 100644 --- a/icu4c/source/common/ucnv.c +++ b/icu4c/source/common/ucnv.c @@ -27,8 +27,6 @@ #include "unicode/uset.h" #include "cmemory.h" #include "cstring.h" -#include "umutex.h" -#include "uhash.h" #include "ustr_imp.h" #include "ucnv_imp.h" #include "ucnv_io.h" @@ -40,18 +38,27 @@ # include void UCNV_DEBUG_LOG(char *what, char *who, void *p, int l) { - static FILE *f = NULL; - if(f==NULL) - { - /* stderr, or open another file */ - f = stderr; - /* f = fopen("c:\\UCNV_DEBUG_LOG.txt", "w"); */ - } + static FILE *f = NULL; + if(f==NULL) + { + /* stderr, or open another file */ + f = stderr; + /* f = fopen("c:\\UCNV_DEBUG_LOG.txt", "w"); */ + } + if (!what) { + what = "(null)"; + } + if (!who) { + who = "(null)"; + } + if (!p) { + p = "(null)"; + } - fprintf(f, "%p\t:%d\t%-20s\t%-10s\n", - p, l, who, what); + fprintf(f, "%p\t:%d\t%-20s\t%-10s\n", + p, l, who, what); - fflush(f); + fflush(f); } @@ -61,11 +68,15 @@ static void UCNV_DEBUG_CNV(UConverter *c, int line) UErrorCode err = U_ZERO_ERROR; fprintf(stderr, "%p\t:%d\t", c, line); if(c!=NULL) { - fprintf(stderr, "%s\t", ucnv_getName(c, &err)); - + const char *name = ucnv_getName(c, &err); + if (!name) { + name = "(null)"; + } + fprintf(stderr, "%s\t", name); + fprintf(stderr, "shr=%p, ref=%x\n", - c->sharedData, - c->sharedData->referenceCounter); + c->sharedData, + c->sharedData->referenceCounter); } else { fprintf(stderr, "DEMISED\n"); } @@ -278,13 +289,7 @@ ucnv_safeClone(const UConverter* cnv, void *stackBuffer, int32_t *pBufferSize, U } /* increment refcount of shared data if needed */ - if (cnv->sharedData->referenceCounter != ~0) { - umtx_lock (NULL); - if (cnv->sharedData->referenceCounter != ~0) { - cnv->sharedData->referenceCounter++; - } - umtx_unlock (NULL); - } + ucnv_incrementRefCount(cnv->sharedData); if(localConverter==NULL || U_FAILURE(*status)) { return NULL; @@ -372,29 +377,20 @@ ucnv_close (UConverter * converter) converter->sharedData->impl->close(converter); } - if (converter->sharedData->referenceCounter != ~0) { - umtx_lock (NULL); - if (converter->sharedData->referenceCounter != 0) { - converter->sharedData->referenceCounter--; - } - umtx_unlock (NULL); - #ifdef UCNV_DEBUG - { - char c[4]; - c[1]=0; - c[0]='0'+converter->sharedData->referenceCounter; - UCNV_DEBUG_LOG("close--", c, converter); - } -#endif - + { + char c[4]; + c[0]='0'+converter->sharedData->referenceCounter; + c[1]=0; + UCNV_DEBUG_LOG("close--", c, converter); if((converter->sharedData->referenceCounter == 0)&&(converter->sharedData->sharedDataCached == FALSE)) { UCNV_DEBUG_CNV(converter); UCNV_DEBUG_LOG("close:delDead", "??", converter); - ucnv_deleteSharedConverterData(converter->sharedData); } } +#endif + ucnv_unloadSharedDataIfReady(converter->sharedData); if(!converter->isCopyLocal){ UCNV_DEBUG_LOG("close:free", "", converter); diff --git a/icu4c/source/common/ucnv_bld.c b/icu4c/source/common/ucnv_bld.c index 4723b51c0c4..cef35198c99 100644 --- a/icu4c/source/common/ucnv_bld.c +++ b/icu4c/source/common/ucnv_bld.c @@ -36,6 +36,7 @@ #include "ustr_imp.h" + #if 0 #include extern void UCNV_DEBUG_LOG(char *what, char *who, void *p, int l); @@ -148,6 +149,27 @@ static UConverterSharedData *createConverterFromFile(const char *pkg, const char static const UConverterSharedData *getAlgorithmicTypeFromName(const char *realName); +/* Stores the shared data in the SHARED_DATA_HASHTABLE + * @param data The shared data + */ +static void ucnv_shareConverterData(UConverterSharedData * data); + +/* gets the shared data from the SHARED_DATA_HASHTABLE (might return NULL if it isn't there) + * @param name The name of the shared data + * @return the shared data from the SHARED_DATA_HASHTABLE + */ +static UConverterSharedData *ucnv_getSharedConverterData(const char *name); + +/* Deletes (frees) the Shared data it's passed. first it checks the referenceCounter to + * see if anyone is using it, if not it frees all the memory stemming from sharedConverterData and + * returns TRUE, + * otherwise returns FALSE + * @param sharedConverterData The shared data + * @return if not it frees all the memory stemming from sharedConverterData and + * returns TRUE, otherwise returns FALSE + */ +static UBool ucnv_deleteSharedConverterData(UConverterSharedData * sharedConverterData); + /** * Un flatten shared data from a UDATA.. */ @@ -281,7 +303,7 @@ getAlgorithmicTypeFromName(const char *realName) /* Puts the shared data in the static hashtable SHARED_DATA_HASHTABLE */ /* Will always be called with the cnvCacheMutex alrady being held */ /* by the calling function. */ -void +static void ucnv_shareConverterData(UConverterSharedData * data) { UErrorCode err = U_ZERO_ERROR; @@ -322,7 +344,7 @@ ucnv_shareConverterData(UConverterSharedData * data) /* Look up a converter name in the shared data cache. */ /* cnvCacheMutex must be held by the caller to protect the hash table. */ -UConverterSharedData * +static UConverterSharedData * ucnv_getSharedConverterData(const char *name) { /*special case when no Table has yet been created we return NULL */ @@ -343,7 +365,7 @@ ucnv_getSharedConverterData(const char *name) /*frees the string of memory blocks associates with a sharedConverter *if and only if the referenceCounter == 0 */ -UBool +static UBool ucnv_deleteSharedConverterData(UConverterSharedData * deadSharedData) { if (deadSharedData->referenceCounter > 0) @@ -383,6 +405,40 @@ ucnv_deleteSharedConverterData(UConverterSharedData * deadSharedData) return TRUE; } +void +ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData) +{ + umtx_lock(&cnvCacheMutex); + /* + Double checking doesn't work on some platforms. + Don't check referenceCounter outside of a mutex block. + */ + if (sharedData->referenceCounter != ~0) { + if (sharedData->referenceCounter > 0) { + sharedData->referenceCounter--; + } + + if((sharedData->referenceCounter <= 0)&&(sharedData->sharedDataCached == FALSE)) { + ucnv_deleteSharedConverterData(sharedData); + } + } + umtx_unlock(&cnvCacheMutex); +} + +void +ucnv_incrementRefCount(UConverterSharedData *sharedData) +{ + umtx_lock(&cnvCacheMutex); + /* + Double checking doesn't work on some platforms. + Don't check referenceCounter outside of a mutex block. + */ + if (sharedData->referenceCounter != ~0) { + sharedData->referenceCounter++; + } + umtx_unlock(&cnvCacheMutex); +} + static void parseConverterOptions(const char *inName, char *cnvName, @@ -472,7 +528,8 @@ ucnv_createConverter(UConverter *myUConverter, const char *converterName, UError const char *realName; UConverterSharedData *mySharedConverterData = NULL; UErrorCode internalErrorCode = U_ZERO_ERROR; - uint32_t options=0; + uint32_t options = 0; + int32_t cnvNumber = -1; if (U_FAILURE (*err)) return NULL; @@ -495,7 +552,7 @@ ucnv_createConverter(UConverter *myUConverter, const char *converterName, UError } /* get the canonical converter name */ - realName = ucnv_io_getConverterName(cnvName, &internalErrorCode); + realName = ucnv_io_getConverterName(cnvName, &cnvNumber, &internalErrorCode); if (U_FAILURE(internalErrorCode) || realName == NULL) { /* * set the input name in case the converter was added @@ -541,9 +598,7 @@ ucnv_createConverter(UConverter *myUConverter, const char *converterName, UError { /* The data for this converter was already in the cache. */ /* Update the reference counter on the shared data: one more client */ - umtx_lock(NULL); mySharedConverterData->referenceCounter++; - umtx_unlock(NULL); } umtx_unlock(&cnvCacheMutex); } @@ -553,9 +608,9 @@ ucnv_createConverter(UConverter *myUConverter, const char *converterName, UError if (U_FAILURE(*err)) { if (mySharedConverterData->referenceCounter != ~0) { - umtx_lock (NULL); + umtx_lock(&cnvCacheMutex); --mySharedConverterData->referenceCounter; - umtx_unlock (NULL); + umtx_unlock(&cnvCacheMutex); } return NULL; } @@ -763,7 +818,7 @@ ucnv_flushCache () * because the sequence of looking up in the cache + incrementing * is protected by cnvCacheMutex. */ - umtx_lock (&cnvCacheMutex); + umtx_lock(&cnvCacheMutex); while ((e = uhash_nextElement (SHARED_DATA_HASHTABLE, &pos)) != NULL) { mySharedData = (UConverterSharedData *) e->value.pointer; @@ -779,7 +834,7 @@ ucnv_flushCache () ucnv_deleteSharedConverterData (mySharedData); } } - umtx_unlock (&cnvCacheMutex); + umtx_unlock(&cnvCacheMutex); ucnv_io_flushAvailableConverterCache(); diff --git a/icu4c/source/common/ucnv_imp.h b/icu4c/source/common/ucnv_imp.h index e36f18583fd..d697a398680 100644 --- a/icu4c/source/common/ucnv_imp.h +++ b/icu4c/source/common/ucnv_imp.h @@ -55,26 +55,19 @@ ucnv_createConverterFromSharedData(UConverter *myUConverter, UConverterSharedDat UConverter* ucnv_createConverterFromPackage(const char *packageName, const char *converterName, UErrorCode *err); -/* Stores the shared data in the SHARED_DATA_HASHTABLE - * @param data The shared data +/** + * This may unload the shared data in a thread safe manner. + * This will only unload the data if no other converters are sharing it. */ -void ucnv_shareConverterData (UConverterSharedData * data); +void +ucnv_unloadSharedDataIfReady(UConverterSharedData *sharedData); -/* gets the shared data from the SHARED_DATA_HASHTABLE (might return NULL if it isn't there) - * @param name The name of the shared data - * @return the shared data from the SHARED_DATA_HASHTABLE +/** + * This is a thread safe way to increment the reference count. */ -UConverterSharedData *ucnv_getSharedConverterData (const char *name); +void +ucnv_incrementRefCount(UConverterSharedData *sharedData); -/* Deletes (frees) the Shared data it's passed. first it checks the referenceCounter to - * see if anyone is using it, if not it frees all the memory stemming from sharedConverterData and - * returns TRUE, - * otherwise returns FALSE - * @param sharedConverterData The shared data - * @return if not it frees all the memory stemming from sharedConverterData and - * returns TRUE, otherwise returns FALSE - */ -UBool ucnv_deleteSharedConverterData (UConverterSharedData * sharedConverterData); /* returns true if "name" is in algorithmicConverterNames * @param name The converter name. diff --git a/icu4c/source/common/ucnv_io.c b/icu4c/source/common/ucnv_io.c index 21b77af8744..1d0a582a114 100644 --- a/icu4c/source/common/ucnv_io.c +++ b/icu4c/source/common/ucnv_io.c @@ -198,12 +198,18 @@ isAcceptable(void *context, static UBool haveAliasData(UErrorCode *pErrorCode) { + int haveData; + if(pErrorCode==NULL || U_FAILURE(*pErrorCode)) { return FALSE; } + umtx_lock(NULL); + haveData = (int)(gAliasData==NULL); + umtx_unlock(NULL); + /* load converter alias data from file if necessary */ - if(gAliasData==NULL) { + if (haveData) { UDataMemory *data = NULL; const uint16_t *table = NULL; uint32_t tableStart; @@ -568,14 +574,20 @@ findTaggedConverterNum(const char *alias, const char *standard, UErrorCode *pErr U_CFUNC const char * -ucnv_io_getConverterName(const char *alias, UErrorCode *pErrorCode) { +ucnv_io_getConverterName(const char *alias, int32_t *convNumPtr, UErrorCode *pErrorCode) { if(haveAliasData(pErrorCode) && isAlias(alias, pErrorCode)) { uint32_t convNum = findConverter(alias, pErrorCode); if (convNum < gConverterListSize) { + if (convNumPtr) { + *convNumPtr = (int32_t)convNum; + } return GET_STRING(gConverterList[convNum]); } /* else converter not found */ } + if (convNumPtr) { + *convNumPtr = -1; + } return NULL; } @@ -1012,7 +1024,7 @@ ucnv_io_setDefaultConverterName(const char *converterName) { gDefaultConverterName=NULL; } else { UErrorCode errorCode=U_ZERO_ERROR; - const char *name=ucnv_io_getConverterName(converterName, &errorCode); + const char *name=ucnv_io_getConverterName(converterName, NULL, &errorCode); if(U_SUCCESS(errorCode) && name!=NULL) { gDefaultConverterName=name; } else { diff --git a/icu4c/source/common/ucnv_io.h b/icu4c/source/common/ucnv_io.h index a236f5b0a0a..5bb471e7219 100644 --- a/icu4c/source/common/ucnv_io.h +++ b/icu4c/source/common/ucnv_io.h @@ -36,11 +36,12 @@ ucnv_io_stripForCompare(char *dst, const char *name); * is returned in mixed-case. * Returns NULL if the alias is not found. * @param alias The alias name to be searched. + * @param convNumPtr aAn out param for the index of the converter name in the alias table. * @param pErrorCode The error code * @return the converter name in mixed-case, return NULL if the alias is not found. */ U_CFUNC const char * -ucnv_io_getConverterName(const char *alias, UErrorCode *pErrorCode); +ucnv_io_getConverterName(const char *alias, int32_t *convNumPtr, UErrorCode *pErrorCode); /** * The count for ucnv_io_getAliases and ucnv_io_getAlias