X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/46f4442e9a5a4f3b98b7c1083586332f6a8a99a4..c5116b9f5a666b9d59f443b3770acd6ef64dc6c3:/icuSources/common/locutil.cpp diff --git a/icuSources/common/locutil.cpp b/icuSources/common/locutil.cpp index e70b6162..3d9d69ff 100644 --- a/icuSources/common/locutil.cpp +++ b/icuSources/common/locutil.cpp @@ -1,9 +1,9 @@ -/** - ******************************************************************************* - * Copyright (C) 2002-2006, International Business Machines Corporation and * - * others. All Rights Reserved. * +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +/* ******************************************************************************* - * + * Copyright (C) 2002-2014, International Business Machines Corporation and + * others. All Rights Reserved. ******************************************************************************* */ #include "unicode/utypes.h" @@ -11,6 +11,7 @@ #if !UCONFIG_NO_SERVICE || !UCONFIG_NO_TRANSLITERATION #include "unicode/resbund.h" +#include "unicode/uenum.h" #include "cmemory.h" #include "ustrfmt.h" #include "locutil.h" @@ -20,7 +21,8 @@ #include "umutex.h" // see LocaleUtility::getAvailableLocaleNames -static U_NAMESPACE_QUALIFIER Hashtable * LocaleUtility_cache = NULL; +static icu::UInitOnce LocaleUtilityInitOnce = U_INITONCE_INITIALIZER; +static icu::Hashtable * LocaleUtility_cache = NULL; #define UNDERSCORE_CHAR ((UChar)0x005f) #define AT_SIGN_CHAR ((UChar)64) @@ -41,6 +43,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 @@ -191,34 +212,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); @@ -227,25 +227,33 @@ LocaleUtility::getAvailableLocaleNames(const UnicodeString& bundleID) if (htp == NULL) { htp = new Hashtable(status); if (htp && U_SUCCESS(status)) { - CharString cbundleID(bundleID); - const char* path = (const char*) cbundleID; - if (*path == 0) path = NULL; // empty string => NULL - UEnumeration *uenum = ures_openAvailableLocales(path, &status); + CharString cbundleID; + cbundleID.appendInvariantChars(bundleID, status); + const char* path = cbundleID.isEmpty() ? NULL : cbundleID.data(); + icu::LocalUEnumerationPointer uenum(ures_openAvailableLocales(path, &status)); for (;;) { - const UChar* id = uenum_unext(uenum, NULL, &status); + const UChar* id = uenum_unext(uenum.getAlias(), NULL, &status); if (id == NULL) { break; } htp->put(UnicodeString(id), (void*)htp, status); } - uenum_close(uenum); if (U_FAILURE(status)) { delete htp; return NULL; } umtx_lock(NULL); - cache->put(bundleID, (void*)htp, status); - umtx_unlock(NULL); + Hashtable *t = static_cast(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;