+ origItem->removeRef();
+}
+
+void UnifiedCacheThread::run() {
+ // Run the exercise with 2 different locales so that we can exercise
+ // eviction more. If each thread exercises just one locale, then
+ // eviction can't start until the threads end.
+ exerciseByLocale(fLoc);
+ exerciseByLocale(fLoc2);
+}
+
+void MultithreadTest::TestUnifiedCache() {
+
+ // Start with our own local cache so that we have complete control
+ // and set the eviction policy to evict starting with 2 unused
+ // values
+ UErrorCode status = U_ZERO_ERROR;
+ UnifiedCache::getInstance(status);
+ UnifiedCache cache(status);
+ cache.setEvictionPolicy(2, 0, status);
+ U_ASSERT(U_SUCCESS(status));
+
+ gCTMutex = new std::mutex();
+ gCTConditionVar = new std::condition_variable();
+
+ gObjectsCreated = 0;
+
+ UnifiedCacheThread *threads[CACHE_LOAD][UPRV_LENGTHOF(gCacheLocales)];
+ for (int32_t i=0; i<CACHE_LOAD; ++i) {
+ for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
+ // Each thread works with a pair of locales.
+ threads[i][j] = new UnifiedCacheThread(
+ &cache, gCacheLocales[j], gCacheLocales2[j]);
+ threads[i][j]->start();
+ }
+ }
+
+ for (int32_t i=0; i<CACHE_LOAD; ++i) {
+ for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
+ threads[i][j]->join();
+ }
+ }
+ // Because of cache eviction, we can't assert exactly how many
+ // distinct objects get created over the course of this run.
+ // However we know that at least 8 objects get created because that
+ // is how many distinct languages we have in our test.
+ if (gObjectsCreated < 8) {
+ errln("%s:%d Too few objects created.", __FILE__, __LINE__);
+ }
+ // We know that each thread cannot create more than 2 objects in
+ // the cache, and there are UPRV_LENGTHOF(gCacheLocales) pairs of
+ // objects fetched from the cache. If the threads run in series because
+ // of eviction, at worst case each thread creates two objects.
+ if (gObjectsCreated > 2 * CACHE_LOAD * UPRV_LENGTHOF(gCacheLocales)) {
+ errln("%s:%d Too many objects created, got %d, expected %d", __FILE__, __LINE__, gObjectsCreated, 2 * CACHE_LOAD * UPRV_LENGTHOF(gCacheLocales));
+
+ }
+
+ assertEquals(WHERE, 2, cache.unusedCount());
+
+ // clean up threads
+ for (int32_t i=0; i<CACHE_LOAD; ++i) {
+ for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
+ delete threads[i][j];
+ }
+ }
+ delete gCTMutex;
+ delete gCTConditionVar;
+}
+
+#if !UCONFIG_NO_TRANSLITERATION
+//
+// BreakTransliterator Threading Test
+// This is a test for bug #11603. Test verified to fail prior to fix.
+//
+
+static const Transliterator *gSharedTransliterator;
+static const UnicodeString *gTranslitInput;
+static const UnicodeString *gTranslitExpected;
+
+class BreakTranslitThread: public SimpleThread {
+ public:
+ BreakTranslitThread() {}
+ ~BreakTranslitThread() {}
+ void run();
+};
+
+void BreakTranslitThread::run() {
+ for (int i=0; i<10; i++) {
+ icu::UnicodeString s(*gTranslitInput);
+ gSharedTransliterator->transliterate(s);
+ if (*gTranslitExpected != s) {
+ IntlTest::gTest->errln("%s:%d Transliteration threading failure.", __FILE__, __LINE__);
+ break;
+ }
+ }
+}
+
+void MultithreadTest::TestBreakTranslit() {
+ UErrorCode status = U_ZERO_ERROR;
+ UnicodeString input(
+ u"\u0E42\u0E14\u0E22\u0E1E\u0E37\u0E49\u0E19\u0E10\u0E32\u0E19\u0E41\u0E25\u0E49\u0E27,");
+ // Thai script, โดยพื้นฐานแล้ว
+ gTranslitInput = &input;
+
+ gSharedTransliterator = Transliterator::createInstance(
+ UnicodeString(u"Any-Latin; Lower; NFD; [:Diacritic:]Remove; NFC; Latin-ASCII;"), UTRANS_FORWARD, status);
+ assertSuccess(WHERE, status);
+ if (!assertTrue(WHERE, gSharedTransliterator != nullptr)) {
+ return;
+ }
+
+ UnicodeString expected(*gTranslitInput);
+ gSharedTransliterator->transliterate(expected);
+ gTranslitExpected = &expected;
+
+ BreakTranslitThread threads[4];
+ for (int i=0; i<UPRV_LENGTHOF(threads); ++i) {
+ threads[i].start();
+ }
+ for (int i=0; i<UPRV_LENGTHOF(threads); ++i) {
+ threads[i].join();
+ }
+
+ delete gSharedTransliterator;
+ gTranslitInput = NULL;
+ gTranslitExpected = NULL;
+}
+
+
+class TestIncDecThread : public SimpleThread {
+public:
+ TestIncDecThread() {}
+ virtual void run();
+};
+
+static u_atomic_int32_t gIncDecCounter;
+
+void TestIncDecThread::run() {
+ umtx_atomic_inc(&gIncDecCounter);
+ for (int32_t i=0; i<5000000; ++i) {
+ umtx_atomic_inc(&gIncDecCounter);
+ umtx_atomic_dec(&gIncDecCounter);
+ }
+}
+
+void MultithreadTest::TestIncDec()
+{
+ static constexpr int NUM_THREADS = 4;
+ gIncDecCounter = 0;
+ TestIncDecThread threads[NUM_THREADS];
+ for (auto &thread:threads) {
+ thread.start();
+ }
+ for (auto &thread:threads) {
+ thread.join();
+ }
+ assertEquals(WHERE, NUM_THREADS, gIncDecCounter);
+}
+
+#if !UCONFIG_NO_FORMATTING
+static Calendar *gSharedCalendar = {};
+
+class Test20104Thread : public SimpleThread {
+public:
+ Test20104Thread() {}
+ virtual void run();
+};
+
+void Test20104Thread::run() {
+ gSharedCalendar->defaultCenturyStartYear();
+}
+
+void MultithreadTest::Test20104() {
+ UErrorCode status = U_ZERO_ERROR;
+ Locale loc("hi_IN");
+ gSharedCalendar = new IndianCalendar(loc, status);
+ assertSuccess(WHERE, status);
+
+ static constexpr int NUM_THREADS = 4;
+ Test20104Thread threads[NUM_THREADS];
+ for (auto &thread:threads) {
+ thread.start();
+ }
+ for (auto &thread:threads) {
+ thread.join();
+ }
+ delete gSharedCalendar;
+ // Note: failure is reported by Thread Sanitizer. Test itself succeeds.