+ delete StringThreadTest2::gSharedString;
+ StringThreadTest2::gSharedString = NULL;
+}
+
+
+//
+// Test for ticket #10673, race in cache code in AnyTransliterator.
+// It's difficult to make the original unsafe code actually fail, but
+// this test will fairly reliably take the code path for races in
+// populating the cache.
+//
+
+#if !UCONFIG_NO_TRANSLITERATION
+Transliterator *gSharedTranslit = NULL;
+class TxThread: public SimpleThread {
+ public:
+ TxThread() {}
+ ~TxThread();
+ void run();
+};
+
+TxThread::~TxThread() {}
+void TxThread::run() {
+ UnicodeString greekString(u"διαφορετικούς");
+ gSharedTranslit->transliterate(greekString);
+ IntlTest::gTest->assertEquals(WHERE, UnicodeString(u"diaphoretikoús"), greekString);
+}
+#endif
+
+
+void MultithreadTest::TestAnyTranslit() {
+#if !UCONFIG_NO_TRANSLITERATION
+ UErrorCode status = U_ZERO_ERROR;
+ LocalPointer<Transliterator> tx(Transliterator::createInstance("Any-Latin", UTRANS_FORWARD, status));
+ if (!assertSuccess(WHERE, status, true)) { return; }
+
+ gSharedTranslit = tx.getAlias();
+ TxThread threads[4];
+ int32_t i;
+ for (i=0; i<UPRV_LENGTHOF(threads); i++) {
+ threads[i].start();
+ }
+
+ for (i=0; i<UPRV_LENGTHOF(threads); i++) {
+ threads[i].join();
+ }
+ gSharedTranslit = NULL;
+#endif // !UCONFIG_NO_TRANSLITERATION
+}
+
+
+
+//
+// Unified Cache Test
+//
+
+// Each thread fetches a pair of objects. There are 8 distinct pairs:
+// ("en_US", "bs"), ("en_GB", "ca"), ("fr_FR", "ca_AD") etc.
+// These pairs represent 8 distinct languages
+
+// Note that only one value per language gets created in the cache.
+// In particular each cached value can have multiple keys.
+static const char *gCacheLocales[] = {
+ "en_US", "en_GB", "fr_FR", "fr",
+ "de", "sr_ME", "sr_BA", "sr_CS"};
+static const char *gCacheLocales2[] = {
+ "bs", "ca", "ca_AD", "ca_ES",
+ "en_US", "fi", "ff_CM", "ff_GN"};
+
+static int32_t gObjectsCreated = 0; // protected by gCTMutex
+static const int32_t CACHE_LOAD = 3;
+
+class UCTMultiThreadItem : public SharedObject {
+ public:
+ char *value;
+ UCTMultiThreadItem(const char *x) : value(NULL) {
+ value = uprv_strdup(x);
+ }
+ virtual ~UCTMultiThreadItem() {
+ uprv_free(value);
+ }
+};
+
+U_NAMESPACE_BEGIN
+
+static std::mutex *gCTMutex = nullptr;
+static std::condition_variable *gCTConditionVar = nullptr;
+
+template<> U_EXPORT
+const UCTMultiThreadItem *LocaleCacheKey<UCTMultiThreadItem>::createObject(
+ const void *context, UErrorCode &status) const {
+ const UnifiedCache *cacheContext = (const UnifiedCache *) context;
+
+ if (uprv_strcmp(fLoc.getLanguage(), fLoc.getName()) != 0) {
+ const UCTMultiThreadItem *result = NULL;
+ if (cacheContext == NULL) {
+ UnifiedCache::getByLocale(fLoc.getLanguage(), result, status);
+ return result;