]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/common/locutil.cpp
ICU-64243.0.1.tar.gz
[apple/icu.git] / icuSources / common / locutil.cpp
index e70b61628cffb01923eaa7df3d2483a955c56343..3d9d69ff7ed0e228951c81b00f0acfaf992d0385 100644 (file)
@@ -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<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;