ICU-2871 Default Locale thread safety enhancements. Add cache of defaults that have been set.
X-SVN-Rev: 12669
This commit is contained in:
parent
da8e68dd39
commit
c78d5ede6d
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user