| Index: source/common/locutil.cpp
|
| diff --git a/source/common/locutil.cpp b/source/common/locutil.cpp
|
| index 69ea4392b19eece8cccf52f115183051430fcc12..e5fe18c4c3c42e74deb738b4d5c0ab106d45b52c 100644
|
| --- a/source/common/locutil.cpp
|
| +++ b/source/common/locutil.cpp
|
| @@ -1,6 +1,6 @@
|
| /*
|
| *******************************************************************************
|
| - * Copyright (C) 2002-2011, International Business Machines Corporation and
|
| + * Copyright (C) 2002-2014, International Business Machines Corporation and
|
| * others. All Rights Reserved.
|
| *******************************************************************************
|
| */
|
| @@ -18,6 +18,7 @@
|
| #include "umutex.h"
|
|
|
| // see LocaleUtility::getAvailableLocaleNames
|
| +static icu::UInitOnce LocaleUtilityInitOnce = U_INITONCE_INITIALIZER;
|
| static icu::Hashtable * LocaleUtility_cache = NULL;
|
|
|
| #define UNDERSCORE_CHAR ((UChar)0x005f)
|
| @@ -39,6 +40,25 @@ static UBool U_CALLCONV service_cleanup(void) {
|
| }
|
| return TRUE;
|
| }
|
| +
|
| +
|
| +static void U_CALLCONV locale_utility_init(UErrorCode &status) {
|
| + using namespace icu;
|
| + U_ASSERT(LocaleUtility_cache == NULL);
|
| + ucln_common_registerCleanup(UCLN_COMMON_SERVICE, service_cleanup);
|
| + LocaleUtility_cache = new Hashtable(status);
|
| + if (U_FAILURE(status)) {
|
| + delete LocaleUtility_cache;
|
| + LocaleUtility_cache = NULL;
|
| + return;
|
| + }
|
| + if (LocaleUtility_cache == NULL) {
|
| + status = U_MEMORY_ALLOCATION_ERROR;
|
| + return;
|
| + }
|
| + LocaleUtility_cache->setValueDeleter(uhash_deleteHashtable);
|
| +}
|
| +
|
| U_CDECL_END
|
|
|
| U_NAMESPACE_BEGIN
|
| @@ -189,34 +209,13 @@ LocaleUtility::getAvailableLocaleNames(const UnicodeString& bundleID)
|
| // garbage ((void*)1 or other random pointer).
|
|
|
| UErrorCode status = U_ZERO_ERROR;
|
| - Hashtable* cache;
|
| - umtx_lock(NULL);
|
| - cache = LocaleUtility_cache;
|
| - umtx_unlock(NULL);
|
| -
|
| + umtx_initOnce(LocaleUtilityInitOnce, locale_utility_init, status);
|
| + Hashtable *cache = LocaleUtility_cache;
|
| if (cache == NULL) {
|
| - cache = new Hashtable(status);
|
| - if (cache == NULL || U_FAILURE(status)) {
|
| - return NULL; // catastrophic failure; e.g. out of memory
|
| - }
|
| - cache->setValueDeleter(uhash_deleteHashtable);
|
| - Hashtable* h; // set this to final LocaleUtility_cache value
|
| - umtx_lock(NULL);
|
| - h = LocaleUtility_cache;
|
| - if (h == NULL) {
|
| - LocaleUtility_cache = h = cache;
|
| - cache = NULL;
|
| - ucln_common_registerCleanup(UCLN_COMMON_SERVICE, service_cleanup);
|
| - }
|
| - umtx_unlock(NULL);
|
| - if(cache != NULL) {
|
| - delete cache;
|
| - }
|
| - cache = h;
|
| + // Catastrophic failure.
|
| + return NULL;
|
| }
|
|
|
| - U_ASSERT(cache != NULL);
|
| -
|
| Hashtable* htp;
|
| umtx_lock(NULL);
|
| htp = (Hashtable*) cache->get(bundleID);
|
| @@ -242,8 +241,17 @@ LocaleUtility::getAvailableLocaleNames(const UnicodeString& bundleID)
|
| return NULL;
|
| }
|
| umtx_lock(NULL);
|
| - cache->put(bundleID, (void*)htp, status);
|
| - umtx_unlock(NULL);
|
| + Hashtable *t = static_cast<Hashtable *>(cache->get(bundleID));
|
| + if (t != NULL) {
|
| + // Another thread raced through this code, creating the cache entry first.
|
| + // Discard ours and return theirs.
|
| + umtx_unlock(NULL);
|
| + delete htp;
|
| + htp = t;
|
| + } else {
|
| + cache->put(bundleID, (void*)htp, status);
|
| + umtx_unlock(NULL);
|
| + }
|
| }
|
| }
|
| return htp;
|
|
|