ICU-2871 Default Locale thread safety enhancements. Add cache of defaults that have been set.

X-SVN-Rev: 12669
This commit is contained in:
Andy Heninger 2003-07-24 01:15:10 +00:00
parent da8e68dd39
commit c78d5ede6d
4 changed files with 173 additions and 49 deletions

View File

@ -67,12 +67,21 @@ typedef enum ELocalePos {
eMAX_LOCALES
} ELocalePos;
/* Use void * to make it properly aligned */
/* Add 1 for rounding */
// static void *gByteLocaleCache[(eMAX_LOCALES + 1) * sizeof(Locale) / sizeof(void*)];
static Locale *gLocaleCache = NULL;
static Locale *gDefaultLocale = NULL;
static Locale *gLocaleCache = NULL;
static const Locale *gDefaultLocale = NULL;
static UHashtable *gDefaultLocalesHashT = NULL;
U_CDECL_BEGIN
//
// Deleter function for Locales owned by the default Locale hash table/
//
static void U_CALLCONV
deleteLocale(void *obj) {
delete (Locale *) obj;
}
U_CDECL_END
UBool
locale_cleanup(void)
@ -89,40 +98,114 @@ locale_cleanup(void)
delete [] gLocaleCache;
gLocaleCache = NULL;
}
if (gDefaultLocale) {
delete gDefaultLocale;
gDefaultLocale = NULL;
if (gDefaultLocalesHashT) {
uhash_close(gDefaultLocalesHashT); // Automatically deletes all elements, using deleter func.
gDefaultLocalesHashT = NULL;
}
gDefaultLocale = NULL;
return TRUE;
}
U_NAMESPACE_BEGIN
const char Locale::fgClassID=0;
//
// locale_set_default_internal.
//
void locale_set_default_internal(const char *id)
{
U_NAMESPACE_USE
Locale tempLocale(Locale::eBOGUS);
UErrorCode status = U_ZERO_ERROR;
if (id == NULL)
{
// If given a NULL string for the locale id, grab the default
// name from the system.
// (Different from most other locale APIs, where a null name means use
// the current ICU default locale.)
if (id == NULL) {
umtx_lock(NULL);
id = uprv_getDefaultLocaleID();
umtx_unlock(NULL);
}
tempLocale.init(id); // Note: we do not want to hold the mutex through init(),
// which is a relatively large, complex function.
// Hence, the use of a temporary locale.
const Locale *defLocale = &Locale::getDefault();
// put the locale id into a canonical form,
// in preparation for looking up this locale in the hash table of
// already-created locale objects.
//
status = U_ZERO_ERROR;
char localeNameBuf[1000];
uloc_getName(id, localeNameBuf, sizeof(localeNameBuf)-1, &status);
localeNameBuf[sizeof(localeNameBuf)-1] = 0; // Force null termination in event of
// a long name filling the buffer.
// (long names are truncated.)
// Lazy creation of the hash table itself, if needed.
//
umtx_lock(NULL);
Locale *ncDefLocale = (Locale *)defLocale;
*ncDefLocale = tempLocale;
UBool hashTableNeedsInit = (gDefaultLocalesHashT == NULL);
umtx_unlock(NULL);
if (hashTableNeedsInit) {
status = U_ZERO_ERROR;
UHashtable *tHashTable = uhash_open(uhash_hashChars, uhash_compareChars, &status);
if (U_FAILURE(status)) {
return;
}
uhash_setValueDeleter(tHashTable, deleteLocale);
umtx_lock(NULL);
if (gDefaultLocalesHashT == NULL) {
gDefaultLocalesHashT = tHashTable;
umtx_unlock(NULL);
} else {
umtx_unlock(NULL);
uhash_close(tHashTable);
}
}
// Hash table lookup, key is the locale full name
umtx_lock(NULL);
Locale *newDefault = (Locale *)uhash_get(gDefaultLocalesHashT, localeNameBuf);
if (newDefault != NULL) {
// We have the requested locale in the hash table already.
// Just set it as default. Inside the mutex lock, for those troublesome processors.
gDefaultLocale = newDefault;
umtx_unlock(NULL);
} else {
umtx_unlock(NULL);
// We haven't seen this locale id before.
// Create a new Locale object for it.
newDefault = new Locale(Locale::eBOGUS);
if (newDefault == NULL) {
// No way to report errors from here.
return;
}
newDefault->init(localeNameBuf);
// Add newly created Locale to the hash table of default Locales
const char *key = newDefault->getName();
U_ASSERT(uprv_strcmp(key, localeNameBuf) == 0);
umtx_lock(NULL);
const Locale *hashTableVal = (const Locale *)uhash_get(gDefaultLocalesHashT, key);
if (hashTableVal == NULL) {
uhash_put(gDefaultLocalesHashT, (void *)key, newDefault, &status);
gDefaultLocale = newDefault;
umtx_unlock(NULL);
// ignore errors from hash table insert. (Couldn't do anything anyway)
// We can still set the default Locale,
// it just wont be cached, and will eventually leak.
} else {
// Some other thread raced us through here, and got the new Locale
// into the hash table before us. Use that one.
gDefaultLocale = hashTableVal; // Assignment to gDefaultLocale must happen inside mutex
umtx_unlock(NULL);
delete newDefault;
}
}
}
U_NAMESPACE_END
/* sfb 07/21/99 */
U_CFUNC void
locale_set_default(const char *id)
@ -465,40 +548,30 @@ Locale::getDefault()
UBool needInit = (gDefaultLocale == NULL);
umtx_unlock(NULL);
if (needInit) {
Locale *tLocale = new Locale(Locale::eBOGUS);
if (tLocale != NULL) {
const char *cLocale;
umtx_lock(NULL);
/* uprv_getDefaultLocaleID is not thread safe, so we surround it with a mutex */
cLocale = uprv_getDefaultLocaleID();
umtx_unlock(NULL);
tLocale->init(cLocale);
umtx_lock(NULL);
if (gDefaultLocale == NULL) {
gDefaultLocale = tLocale;
tLocale = NULL;
}
umtx_unlock(NULL);
delete tLocale;
}
umtx_lock(NULL);
/* uprv_getDefaultLocaleID is not thread safe, so we surround it with a mutex */
const char *cLocale = uprv_getDefaultLocaleID();
umtx_unlock(NULL);
locale_set_default_internal(cLocale);
}
return *gDefaultLocale;
}
void
Locale::setDefault( const Locale& newLocale,
UErrorCode& status)
{
if (U_FAILURE(status))
if (U_FAILURE(status)) {
return;
}
const Locale *defLocale = &Locale::getDefault();
umtx_lock(NULL);
Locale *ncDefLocale = (Locale *)defLocale;
*ncDefLocale = newLocale;
umtx_unlock(NULL);
/* Set the default from the full name string of the supplied locale.
* This is a convenient way to access the default locale caching mechanisms.
*/
const char *localeID = newLocale.getName();
locale_set_default_internal(localeID);
}
Locale

View File

@ -325,7 +325,8 @@ public:
* setDefault() only changes ICU's default locale ID, <strong>not</strong>
* the default locale ID of the runtime environment.
*
* @param newLocale Locale to set to.
* @param newLocale Locale to set to. If NULL, set to the value obtained
* from the runtime environement.
* @param success The error code.
* @system
* @stable ICU 2.0

View File

@ -286,10 +286,12 @@ typedef enum {
/**
* Gets ICU's default locale. This pointer and/or the contents of the pointer may
* become invalid if the uloc_setDefault() is called, so copy the contents of the
* pointer before calling uloc_setDefault().
*
* Gets ICU's default locale.
* The returned string is a snapshot in time, and will remain valid
* and unchanged even when uloc_setDefault() is called.
* The returned storage is owned by ICU, and must not be altered or deleted
* by the caller.
*
* @return the ICU default locale
* @system
* @stable ICU 2.0
@ -298,7 +300,15 @@ U_CAPI const char* U_EXPORT2
uloc_getDefault(void);
/**
* Sets ICU's default locale. Call this once during setup or program initialization.
* Sets ICU's default locale.
* By default (without calling this function), ICU's default locale will be based
* on information obtained from the underlying system environment.
* <p>
* Changes to ICU's default locale do not propagate back to the
* system environment.
* <p>
* Changes to ICU's default locale to not affect any ICU services that
* may already be open based on the previous default locale value.
*
* @param localeID the new ICU default locale. A value of NULL will try to get
* the system's default locale.

View File

@ -238,7 +238,6 @@ static void TestBasicGetters() {
log_err(" Mismatch in getName: %s versus %s\n", name, rawData2[NAME][i]);
}
free(temp);
free(name);
@ -259,6 +258,47 @@ static void TestNullDefault() {
if (uprv_strcmp(uloc_getDefault(), original) != 0) {
log_err(" uloc_setDefault(NULL, &status) didn't get the default locale back!\n");
}
{
/* Test that set & get of default locale work, and that
* default locales are cached and reused, and not overwritten.
*/
const char *n_en_US;
const char *n_fr_FR;
const char *n2_en_US;
status = U_ZERO_ERROR;
uloc_setDefault("en_US", &status);
n_en_US = uloc_getDefault();
if (strcmp(n_en_US, "en_US") != 0) {
log_err("Wrong result from uloc_getDefault(). Expected \"en_US\", got \"%s\"\n", n_en_US);
}
uloc_setDefault("fr_FR", &status);
n_fr_FR = uloc_getDefault();
if (strcmp(n_en_US, "en_US") != 0) {
log_err("uloc_setDefault altered previously default string."
"Expected \"en_US\", got \"%s\"\n", n_en_US);
}
if (strcmp(n_fr_FR, "fr_FR") != 0) {
log_err("Wrong result from uloc_getDefault(). Expected \"fr_FR\", got %s\n", n_fr_FR);
}
uloc_setDefault("en_US", &status);
n2_en_US = uloc_getDefault();
if (strcmp(n2_en_US, "en_US") != 0) {
log_err("Wrong result from uloc_getDefault(). Expected \"en_US\", got \"%s\"\n", n_en_US);
}
if (n2_en_US != n_en_US) {
log_err("Default locale cache failed to reuse en_US locale.\n");
}
if (U_FAILURE(status)) {
log_err("Failure returned from uloc_setDefault - \"%s\"\n", u_errorName(status));
}
}
}
/* Test the i- and x- and @ and . functionality
*/