ICU-11908 NumberingSystem, fix the memory management of static cache of numsys names.
Add thread safe cache initialization.
diff --git a/icu4c/source/i18n/numsys.cpp b/icu4c/source/i18n/numsys.cpp
index 162c50a..50be63e 100644
--- a/icu4c/source/i18n/numsys.cpp
+++ b/icu4c/source/i18n/numsys.cpp
@@ -26,6 +26,8 @@
 #include "unicode/numsys.h"
 #include "cstring.h"
 #include "uassert.h"
+#include "ucln_in.h"
+#include "umutex.h"
 #include "uresimp.h"
 #include "numsys_impl.h"
 
@@ -266,75 +268,82 @@
     return ( algorithmic );
 }
 
-StringEnumeration* NumberingSystem::getAvailableNames(UErrorCode &status) {
-    // TODO(ticket #11908): Init-once static cache, with u_cleanup() callback.
-    static StringEnumeration* availableNames = nullptr;
+namespace {
 
-    if (U_FAILURE(status)) {
-        return nullptr;
-    }
+UVector* gNumsysNames = nullptr;
+UInitOnce gNumSysInitOnce = U_INITONCE_INITIALIZER;
 
-    if ( availableNames == nullptr ) {
-        // TODO: Simple array of UnicodeString objects, based on length of table resource?
-        LocalPointer<UVector> numsysNames(new UVector(uprv_deleteUObject, nullptr, status), status);
-        if (U_FAILURE(status)) {
-            return nullptr;
-        }
-        
-        UErrorCode rbstatus = U_ZERO_ERROR;
-        UResourceBundle *numberingSystemsInfo = ures_openDirect(nullptr, "numberingSystems", &rbstatus);
-        numberingSystemsInfo = ures_getByKey(numberingSystemsInfo, "numberingSystems", numberingSystemsInfo, &rbstatus);
-        if (U_FAILURE(rbstatus)) {
-            // Don't stomp on the catastrophic failure of OOM.
-            if (rbstatus == U_MEMORY_ALLOCATION_ERROR) {
-                status = rbstatus;
-            } else {
-                status = U_MISSING_RESOURCE_ERROR;
-            }
-            ures_close(numberingSystemsInfo);
-            return nullptr;
-        }
-
-        while ( ures_hasNext(numberingSystemsInfo) && U_SUCCESS(status) ) {
-            LocalUResourceBundlePointer nsCurrent(ures_getNextResource(numberingSystemsInfo, nullptr, &rbstatus));
-            if (rbstatus == U_MEMORY_ALLOCATION_ERROR) {
-                status = rbstatus; // we want to report OOM failure back to the caller.
-                break;
-            }
-            const char *nsName = ures_getKey(nsCurrent.getAlias());
-            LocalPointer<UnicodeString> newElem(new UnicodeString(nsName, -1, US_INV), status);
-            if (U_SUCCESS(status)) {
-                numsysNames->addElement(newElem.getAlias(), status);
-                if (U_SUCCESS(status)) {
-                    newElem.orphan(); // on success, the numsysNames vector owns newElem.
-                }
-            }
-        }
-
-        ures_close(numberingSystemsInfo);
-        if (U_FAILURE(status)) {
-            return nullptr;
-        }
-        availableNames = new NumsysNameEnumeration(numsysNames.getAlias(), status);
-        if (availableNames == nullptr) {
-            status = U_MEMORY_ALLOCATION_ERROR;
-            return nullptr;
-        }
-        numsysNames.orphan();  // The names got adopted.
-    }
-
-    return availableNames;
+U_CFUNC UBool U_CALLCONV numSysCleanup() {
+    delete gNumsysNames;
+    gNumsysNames = nullptr;
+    gNumSysInitOnce.reset();
+    return true;
 }
 
-NumsysNameEnumeration::NumsysNameEnumeration(UVector *numsysNames, UErrorCode& /*status*/) {
-    pos=0;
-    fNumsysNames = numsysNames;
+U_CFUNC void initNumsysNames(UErrorCode &status) {
+    U_ASSERT(gNumsysNames == nullptr);
+    ucln_i18n_registerCleanup(UCLN_I18N_NUMSYS, numSysCleanup);
+
+    // TODO: Simple array of UnicodeString objects, based on length of table resource?
+    LocalPointer<UVector> numsysNames(new UVector(uprv_deleteUObject, nullptr, status), status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+
+    UErrorCode rbstatus = U_ZERO_ERROR;
+    UResourceBundle *numberingSystemsInfo = ures_openDirect(nullptr, "numberingSystems", &rbstatus);
+    numberingSystemsInfo =
+            ures_getByKey(numberingSystemsInfo, "numberingSystems", numberingSystemsInfo, &rbstatus);
+    if (U_FAILURE(rbstatus)) {
+        // Don't stomp on the catastrophic failure of OOM.
+        if (rbstatus == U_MEMORY_ALLOCATION_ERROR) {
+            status = rbstatus;
+        } else {
+            status = U_MISSING_RESOURCE_ERROR;
+        }
+        ures_close(numberingSystemsInfo);
+        return;
+    }
+
+    while ( ures_hasNext(numberingSystemsInfo) && U_SUCCESS(status) ) {
+        LocalUResourceBundlePointer nsCurrent(ures_getNextResource(numberingSystemsInfo, nullptr, &rbstatus));
+        if (rbstatus == U_MEMORY_ALLOCATION_ERROR) {
+            status = rbstatus; // we want to report OOM failure back to the caller.
+            break;
+        }
+        const char *nsName = ures_getKey(nsCurrent.getAlias());
+        LocalPointer<UnicodeString> newElem(new UnicodeString(nsName, -1, US_INV), status);
+        if (U_SUCCESS(status)) {
+            numsysNames->addElement(newElem.getAlias(), status);
+            if (U_SUCCESS(status)) {
+                newElem.orphan(); // on success, the numsysNames vector owns newElem.
+            }
+        }
+    }
+
+    ures_close(numberingSystemsInfo);
+    if (U_SUCCESS(status)) {
+        gNumsysNames = numsysNames.orphan();
+    }
+    return;
+}
+
+}   // end anonymous namespace
+
+StringEnumeration* NumberingSystem::getAvailableNames(UErrorCode &status) {
+    umtx_initOnce(gNumSysInitOnce, &initNumsysNames, status);
+    LocalPointer<StringEnumeration> result(new NumsysNameEnumeration(status), status);
+    return result.orphan();
+}
+
+NumsysNameEnumeration::NumsysNameEnumeration(UErrorCode& status) : pos(0) {
+    (void)status;
 }
 
 const UnicodeString*
 NumsysNameEnumeration::snext(UErrorCode& status) {
-    if (U_SUCCESS(status) && (fNumsysNames != nullptr) && (pos < fNumsysNames->size())) {
-        return (const UnicodeString*)fNumsysNames->elementAt(pos++);
+    if (U_SUCCESS(status) && (gNumsysNames != nullptr) && (pos < gNumsysNames->size())) {
+        return (const UnicodeString*)gNumsysNames->elementAt(pos++);
     }
     return nullptr;
 }
@@ -346,11 +355,10 @@
 
 int32_t
 NumsysNameEnumeration::count(UErrorCode& /*status*/) const {
-    return (fNumsysNames==nullptr) ? 0 : fNumsysNames->size();
+    return (gNumsysNames==nullptr) ? 0 : gNumsysNames->size();
 }
 
 NumsysNameEnumeration::~NumsysNameEnumeration() {
-    delete fNumsysNames;
 }
 U_NAMESPACE_END
 
diff --git a/icu4c/source/i18n/numsys_impl.h b/icu4c/source/i18n/numsys_impl.h
index b798286..e76e634 100644
--- a/icu4c/source/i18n/numsys_impl.h
+++ b/icu4c/source/i18n/numsys_impl.h
@@ -26,18 +26,16 @@
 
 class NumsysNameEnumeration : public StringEnumeration {
 public:
-    // NumsysNameEnumeration instance adopts numsysNames
-    NumsysNameEnumeration(UVector *numsysNames, UErrorCode& status);
+    NumsysNameEnumeration(UErrorCode& status);
 
     virtual ~NumsysNameEnumeration();
     static UClassID U_EXPORT2 getStaticClassID(void);
-    virtual UClassID getDynamicClassID(void) const;
-    virtual const UnicodeString* snext(UErrorCode& status);
-    virtual void reset(UErrorCode& status);
-    virtual int32_t count(UErrorCode& status) const;
+    virtual UClassID getDynamicClassID(void) const override;
+    virtual const UnicodeString* snext(UErrorCode& status) override;
+    virtual void reset(UErrorCode& status) override;
+    virtual int32_t count(UErrorCode& status) const override;
 private:
     int32_t pos;
-    UVector *fNumsysNames = nullptr;
 };
 
 U_NAMESPACE_END
diff --git a/icu4c/source/i18n/ucln_in.h b/icu4c/source/i18n/ucln_in.h
index 4c13b9f..2f70a85 100644
--- a/icu4c/source/i18n/ucln_in.h
+++ b/icu4c/source/i18n/ucln_in.h
@@ -60,6 +60,7 @@
     UCLN_I18N_CDFINFO,
     UCLN_I18N_REGION,
     UCLN_I18N_LIST_FORMATTER,
+    UCLN_I18N_NUMSYS,
     UCLN_I18N_COUNT /* This must be last */
 } ECleanupI18NType;
 
diff --git a/icu4c/source/i18n/unicode/numsys.h b/icu4c/source/i18n/unicode/numsys.h
index f1c1090..5e14ea5 100644
--- a/icu4c/source/i18n/unicode/numsys.h
+++ b/icu4c/source/i18n/unicode/numsys.h
@@ -109,6 +109,10 @@
     /**
      * Return a StringEnumeration over all the names of numbering systems known to ICU.
      * The numbering system names will be in alphabetical (invariant) order.
+     *
+     * The returned StringEnumeration is owned by the caller, who must delete it when
+     * finished with it.
+     *
      * @stable ICU 4.2
      */
      static StringEnumeration * U_EXPORT2 getAvailableNames(UErrorCode& status);
diff --git a/icu4c/source/test/cintltst/cnumtst.c b/icu4c/source/test/cintltst/cnumtst.c
index 0abdd67..b4d3a58 100644
--- a/icu4c/source/test/cintltst/cnumtst.c
+++ b/icu4c/source/test/cintltst/cnumtst.c
@@ -2500,33 +2500,37 @@
         }
     }
 
-    status = U_ZERO_ERROR;
-    uenum = unumsys_openAvailableNames(&status);
-    if ( U_SUCCESS(status) ) {
-        int32_t numsysCount = 0;
-        // sanity check for a couple of number systems that must be in the enumeration
-        UBool foundLatn = FALSE;
-        UBool foundArab = FALSE;
-        while ( (numsys = uenum_next(uenum, NULL, &status)) != NULL && U_SUCCESS(status) ) {
-            status = U_ZERO_ERROR;
-            unumsys = unumsys_openByName(numsys, &status);
-            if ( U_SUCCESS(status) ) {
-                numsysCount++;
-                if ( uprv_strcmp(numsys, "latn") ) foundLatn = TRUE;
-                if ( uprv_strcmp(numsys, "arab") ) foundArab = TRUE;
-                unumsys_close(unumsys);
-            } else {
-                log_err("unumsys_openAvailableNames includes %s but unumsys_openByName on it fails with status %s\n",
-                        numsys, myErrorName(status));
+    for (int i=0; i<3; ++i) {
+        // Run the test of unumsys_openAvailableNames() multiple times.
+        // Helps verify the management of the internal cache of the names.
+        status = U_ZERO_ERROR;
+        uenum = unumsys_openAvailableNames(&status);
+        if ( U_SUCCESS(status) ) {
+            int32_t numsysCount = 0;
+            // sanity check for a couple of number systems that must be in the enumeration
+            UBool foundLatn = FALSE;
+            UBool foundArab = FALSE;
+            while ( (numsys = uenum_next(uenum, NULL, &status)) != NULL && U_SUCCESS(status) ) {
+                status = U_ZERO_ERROR;
+                unumsys = unumsys_openByName(numsys, &status);
+                if ( U_SUCCESS(status) ) {
+                    numsysCount++;
+                    if ( uprv_strcmp(numsys, "latn") ) foundLatn = TRUE;
+                    if ( uprv_strcmp(numsys, "arab") ) foundArab = TRUE;
+                    unumsys_close(unumsys);
+                } else {
+                    log_err("unumsys_openAvailableNames includes %s but unumsys_openByName on it fails with status %s\n",
+                            numsys, myErrorName(status));
+                }
             }
+            uenum_close(uenum);
+            if ( numsysCount < 40 || !foundLatn || !foundArab ) {
+                log_err("unumsys_openAvailableNames results incomplete: numsysCount %d, foundLatn %d, foundArab %d\n",
+                        numsysCount, foundLatn, foundArab);
+            }
+        } else {
+            log_data_err("unumsys_openAvailableNames fails with status %s\n", myErrorName(status));
         }
-        uenum_close(uenum);
-        if ( numsysCount < 40 || !foundLatn || !foundArab ) {
-            log_err("unumsys_openAvailableNames results incomplete: numsysCount %d, foundLatn %d, foundArab %d\n",
-                    numsysCount, foundLatn, foundArab);
-        }
-    } else {
-        log_data_err("unumsys_openAvailableNames fails with status %s\n", myErrorName(status));
     }
 }