]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/test/intltest/tsmthred.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / test / intltest / tsmthred.cpp
index 036d5e1d3551237073dd0ded1c902f2dcef450df..98e04869d8593344bcfb9ac6ae88565d926a004f 100644 (file)
@@ -13,6 +13,7 @@
 #include "umutex.h"
 #include "cmemory.h"
 #include "cstring.h"
+#include "indiancal.h"
 #include "uparse.h"
 #include "unicode/localpointer.h"
 #include "unicode/resbund.h"
 #include "uassert.h"
 
 
-#define TSMTHREAD_FAIL(msg) errln("%s at file %s, line %d", msg, __FILE__, __LINE__)
-#define TSMTHREAD_ASSERT(expr) {if (!(expr)) {TSMTHREAD_FAIL("Fail");}}
-#define TSMTHREAD_ASSERT_SUCCESS(status) {if (U_FAILURE(status)) { \
-                  errln("file: %s:%d status = %s\n", __FILE__, __LINE__, u_errorName(status));}}
-
 MultithreadTest::MultithreadTest()
 {
 }
@@ -45,6 +41,7 @@ MultithreadTest::~MultithreadTest()
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>    // tolower, toupper
+#include <memory>
 
 #include "unicode/putil.h"
 
@@ -62,82 +59,27 @@ void MultithreadTest::runIndexedTest( int32_t index, UBool exec,
                 const char* &name, char* /*par*/ ) {
     if (exec)
         logln("TestSuite MultithreadTest: ");
-    switch (index) {
-    case 0:
-        name = "TestThreads";
-        if (exec)
-            TestThreads();
-        break;
-
-    case 1:
-        name = "TestMutex";
-        if (exec)
-            TestMutex();
-        break;
-
-    case 2:
-        name = "TestThreadedIntl";
+
+    TESTCASE_AUTO_BEGIN;
+    TESTCASE_AUTO(TestThreads);
 #if !UCONFIG_NO_FORMATTING
-        if (exec) {
-            TestThreadedIntl();
-        }
+    TESTCASE_AUTO(TestThreadedIntl);
 #endif
-        break;
-
-    case 3:
-      name = "TestCollators";
 #if !UCONFIG_NO_COLLATION
-      if (exec) {
-            TestCollators();
-      }
+    TESTCASE_AUTO(TestCollators);
 #endif /* #if !UCONFIG_NO_COLLATION */
-      break;
-
-    case 4:
-        name = "TestString";
-        if (exec) {
-            TestString();
-        }
-        break;
-
-    case 5:
-        name = "TestArabicShapingThreads";
-        if (exec) {
-            TestArabicShapingThreads();
-        }
-        break;
-
-    case 6:
-        name = "TestAnyTranslit";
-        if (exec) {
-            TestAnyTranslit();
-        }
-        break;
-
-    case 7:
-        name = "TestConditionVariables";
-        if (exec) {
-            TestConditionVariables();
-        }
-        break;
-    case 8:
-        name = "TestUnifiedCache";
-        if (exec) {
-            TestUnifiedCache();
-        }
-        break;
+    TESTCASE_AUTO(TestString);
+    TESTCASE_AUTO(TestArabicShapingThreads);
+    TESTCASE_AUTO(TestAnyTranslit);
+    TESTCASE_AUTO(TestUnifiedCache);
 #if !UCONFIG_NO_TRANSLITERATION
-    case 9:
-        name = "TestBreakTranslit";
-        if (exec) {
-            TestBreakTranslit();
-        }
-        break;
-#endif
-    default:
-        name = "";
-        break; //needed to end loop
-    }
+    TESTCASE_AUTO(TestBreakTranslit);
+    TESTCASE_AUTO(TestIncDec);
+#if !UCONFIG_NO_FORMATTING
+    TESTCASE_AUTO(Test20104);
+#endif /* #if !UCONFIG_NO_FORMATTING */
+#endif /* #if !UCONFIG_NO_TRANSLITERATION */
+    TESTCASE_AUTO_END;
 }
 
 
@@ -189,10 +131,7 @@ void MultithreadTest::TestThreads()
         logln(" Subthread started.");
     }
 
-    if (numThreadsStarted != THREADTEST_NRTHREADS) {
-        errln("Not all threads could be started for testing!");
-        return;
-    }
+    assertTrue(WHERE, THREADTEST_NRTHREADS == numThreadsStarted);
 
     logln("Waiting for threads to be set..");
     for(i=0; i<THREADTEST_NRTHREADS; i++) {
@@ -219,8 +158,8 @@ void MultithreadTest::TestThreads()
 class TestArabicShapeThreads : public SimpleThread
 {
 public:
-    TestArabicShapeThreads() {};
-    virtual void run() { doTailTest(); };
+    TestArabicShapeThreads() {}
+    virtual void run() { doTailTest(); }
 private:
        void doTailTest();
 };
@@ -251,7 +190,7 @@ void TestArabicShapeThreads::doTailTest(void) {
         }
 
 
-        //"Trying new tail
+        // Trying new tail
         status = U_ZERO_ERROR;
         length = u_shapeArabic(src, -1, dst, UPRV_LENGTHOF(dst),
                 U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR|U_SHAPE_TAIL_NEW_UNICODE, &status);
@@ -291,103 +230,6 @@ void MultithreadTest::TestArabicShapingThreads()
 }
 
 
-//-----------------------------------------------------------------------
-//
-//  TestMutex  - a simple (non-stress) test to verify that ICU mutexes
-//               and condition variables are functioning.  Does not test the use of
-//               mutexes within ICU services, but rather that the
-//               platform's mutex support is at least superficially there.
-//
-//----------------------------------------------------------------------
-static UMutex         gTestMutexA          = U_MUTEX_INITIALIZER;
-static UConditionVar  gThreadsCountChanged = U_CONDITION_INITIALIZER;
-
-static int     gThreadsStarted = 0;
-static int     gThreadsInMiddle = 0;
-static int     gThreadsDone = 0;
-
-static const int TESTMUTEX_THREAD_COUNT = 40;
-
-class TestMutexThread : public SimpleThread
-{
-public:
-    virtual void run() {
-        // This is the code that each of the spawned threads runs.
-        // All threads move together throught the started - middle - done sequence together,
-        // waiting for all other threads to reach each point before advancing.
-        umtx_lock(&gTestMutexA);
-        gThreadsStarted += 1;
-        umtx_condBroadcast(&gThreadsCountChanged);
-        while (gThreadsStarted < TESTMUTEX_THREAD_COUNT) {
-            if (gThreadsInMiddle != 0) {
-                IntlTest::gTest->errln(
-                    "%s:%d gThreadsInMiddle = %d. Expected 0.", __FILE__, __LINE__, gThreadsInMiddle);
-                return;
-            }
-            umtx_condWait(&gThreadsCountChanged, &gTestMutexA);
-        }
-
-        gThreadsInMiddle += 1;
-        umtx_condBroadcast(&gThreadsCountChanged);
-        while (gThreadsInMiddle < TESTMUTEX_THREAD_COUNT) {
-            if (gThreadsDone != 0) {
-                IntlTest::gTest->errln(
-                    "%s:%d gThreadsDone = %d. Expected 0.", __FILE__, __LINE__, gThreadsDone);
-                return;
-            }
-            umtx_condWait(&gThreadsCountChanged, &gTestMutexA);
-        }
-
-        gThreadsDone += 1;
-        umtx_condBroadcast(&gThreadsCountChanged);
-        while (gThreadsDone < TESTMUTEX_THREAD_COUNT) {
-            umtx_condWait(&gThreadsCountChanged, &gTestMutexA);
-        }
-        umtx_unlock(&gTestMutexA);
-    }
-};
-
-void MultithreadTest::TestMutex()
-{
-    gThreadsStarted = 0;
-    gThreadsInMiddle = 0;
-    gThreadsDone = 0;
-    int32_t i = 0;
-    TestMutexThread  threads[TESTMUTEX_THREAD_COUNT];
-    umtx_lock(&gTestMutexA);
-    for (i=0; i<TESTMUTEX_THREAD_COUNT; i++) {
-        if (threads[i].start() != 0) {
-            errln("%s:%d Error starting thread %d", __FILE__, __LINE__, i);
-            return;
-        }
-    }
-
-    // Because we are holding gTestMutexA, all of the threads should be blocked
-    // at the start of their run() function.
-    if (gThreadsStarted != 0) {
-        errln("%s:%d gThreadsStarted=%d. Expected 0.", __FILE__, __LINE__, gThreadsStarted);
-        return;
-    }
-
-    while (gThreadsInMiddle < TESTMUTEX_THREAD_COUNT) {
-        if (gThreadsDone != 0) {
-            errln("%s:%d gThreadsDone=%d. Expected 0.", __FILE__, __LINE__, gThreadsStarted);
-            return;
-        }
-        umtx_condWait(&gThreadsCountChanged, &gTestMutexA);
-    }
-
-    while (gThreadsDone < TESTMUTEX_THREAD_COUNT) {
-        umtx_condWait(&gThreadsCountChanged, &gTestMutexA);
-    }
-    umtx_unlock(&gTestMutexA);
-
-    for (i=0; i<TESTMUTEX_THREAD_COUNT; i++) {
-        threads[i].join();
-    }
-}
-
-
 //-------------------------------------------------------------------------------------------
 //
 //   TestMultithreadedIntl.  Test ICU Formatting in a multi-threaded environment
@@ -399,26 +241,26 @@ void MultithreadTest::TestMutex()
 UnicodeString showDifference(const UnicodeString& expected, const UnicodeString& result)
 {
     UnicodeString res;
-    res = expected + "<Expected\n";
+    res = expected + u"<Expected\n";
     if(expected.length() != result.length())
-        res += " [ Different lengths ] \n";
+        res += u" [ Different lengths ] \n";
     else
     {
         for(int32_t i=0;i<expected.length();i++)
         {
             if(expected[i] == result[i])
             {
-                res += " ";
+                res += u" ";
             }
             else
             {
-                res += "|";
+                res += u"|";
             }
         }
-        res += "<Differences";
-        res += "\n";
+        res += u"<Differences";
+        res += u"\n";
     }
-    res += result + "<Result\n";
+    res += result + u"<Result\n";
 
     return res;
 }
@@ -448,7 +290,8 @@ struct FormatThreadTestData
 // "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3 number,currency}."
 
 static void formatErrorMessage(UErrorCode &realStatus, const UnicodeString& pattern, const Locale& theLocale,
-                     UErrorCode inStatus0, /* statusString 1 */ const Locale &inCountry2, double currency3, // these numbers are the message arguments.
+                     UErrorCode inStatus0,                       // statusString 1
+                     const Locale &inCountry2, double currency3, // these numbers are the message arguments.
                      UnicodeString &result)
 {
     if(U_FAILURE(realStatus))
@@ -466,19 +309,15 @@ static void formatErrorMessage(UErrorCode &realStatus, const UnicodeString& patt
         Formattable(currency3)// currency3  {3,number,currency}
     };
 
-    MessageFormat *fmt = new MessageFormat("MessageFormat's API is broken!!!!!!!!!!!",realStatus);
-    fmt->setLocale(theLocale);
-    fmt->applyPattern(pattern, realStatus);
-
+    LocalPointer<MessageFormat> fmt(new MessageFormat(u"MessageFormat's API is broken!!!!!!!!!!!",realStatus), realStatus);
     if (U_FAILURE(realStatus)) {
-        delete fmt;
         return;
     }
+    fmt->setLocale(theLocale);
+    fmt->applyPattern(pattern, realStatus);
 
     FieldPosition ignore = 0;
     fmt->format(myArgs,4,result,ignore,realStatus);
-
-    delete fmt;
 }
 
 /**
@@ -501,8 +340,8 @@ const ThreadSafeFormatSharedData *gSharedData = NULL;
 
 ThreadSafeFormatSharedData::ThreadSafeFormatSharedData(UErrorCode &status) {
     fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status));
-    static const UChar kYDD[] = { 0x59, 0x44, 0x44, 0x00 };
-    static const UChar kBBD[] = { 0x42, 0x42, 0x44, 0x00 };
+    static const UChar *kYDD = u"YDD";
+    static const UChar *kBBD = u"BBD";
     fYDDThing.adoptObject(new CurrencyAmount(123.456, kYDD, status));
     fBBDThing.adoptObject(new CurrencyAmount(987.654, kBBD, status));
     if (U_FAILURE(status)) {
@@ -538,26 +377,26 @@ ThreadSafeFormat::ThreadSafeFormat(UErrorCode &status) {
   fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status));
 }
 
-static const UChar kUSD[] = { 0x55, 0x53, 0x44, 0x00 };
+static const UChar *kUSD = u"USD";
 
 UBool ThreadSafeFormat::doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const {
   UBool okay = TRUE;
 
   if(u_strcmp(fFormat->getCurrency(), kUSD)) {
-    appendErr.append("fFormat currency != ")
+    appendErr.append(u"fFormat currency != ")
       .append(kUSD)
-      .append(", =")
+      .append(u", =")
       .append(fFormat->getCurrency())
-      .append("! ");
+      .append(u"! ");
     okay = FALSE;
   }
 
   if(u_strcmp(gSharedData->fFormat->getCurrency(), kUSD)) {
-    appendErr.append("gFormat currency != ")
+    appendErr.append(u"gFormat currency != ")
       .append(kUSD)
-      .append(", =")
+      .append(u", =")
       .append(gSharedData->fFormat->getCurrency())
-      .append("! ");
+      .append(u"! ");
     okay = FALSE;
   }
   UnicodeString str;
@@ -666,13 +505,13 @@ public:
         // Keep this data here to avoid static initialization.
         FormatThreadTestData kNumberFormatTestData[] =
         {
-            FormatThreadTestData((double)5.0, UnicodeString("5", "")),
-                FormatThreadTestData( 6.0, UnicodeString("6", "")),
-                FormatThreadTestData( 20.0, UnicodeString("20", "")),
-                FormatThreadTestData( 8.0, UnicodeString("8", "")),
-                FormatThreadTestData( 8.3, UnicodeString("8.3", "")),
-                FormatThreadTestData( 12345, UnicodeString("12,345", "")),
-                FormatThreadTestData( 81890.23, UnicodeString("81,890.23", "")),
+            FormatThreadTestData((double)5.0, UnicodeString(u"5")),
+                FormatThreadTestData( 6.0, UnicodeString(u"6")),
+                FormatThreadTestData( 20.0, UnicodeString(u"20")),
+                FormatThreadTestData( 8.0, UnicodeString(u"8")),
+                FormatThreadTestData( 8.3, UnicodeString(u"8.3")),
+                FormatThreadTestData( 12345, UnicodeString(u"12,345")),
+                FormatThreadTestData( 81890.23, UnicodeString(u"81,890.23")),
         };
         int32_t kNumberFormatTestDataLength = UPRV_LENGTHOF(kNumberFormatTestData);
 
@@ -683,9 +522,9 @@ public:
                 FormatThreadTestData( 1.0, CharsToUnicodeString("100\\u00a0%")),
                 FormatThreadTestData( 0.26, CharsToUnicodeString("26\\u00a0%")),
                 FormatThreadTestData(
-                   16384.99, CharsToUnicodeString("1\\u00a0638\\u00a0499\\u00a0%")), // U+00a0 = NBSP
+                   16384.99, CharsToUnicodeString("1\\u202F638\\u202F499\\u00a0%")), // U+202F = NNBSP
                 FormatThreadTestData(
-                    81890.23, CharsToUnicodeString("8\\u00a0189\\u00a0023\\u00a0%")),
+                    81890.23, CharsToUnicodeString("8\\u202F189\\u202F023\\u00a0%")),
         };
         int32_t kPercentFormatTestDataLength = UPRV_LENGTHOF(kPercentFormatTestData);
         int32_t iteration;
@@ -748,38 +587,36 @@ public:
             default:
             case 0:
                 statusToCheck=                      U_FILE_ACCESS_ERROR;
-                patternToCheck=        "0:Someone from {2} is receiving a #{0}"
-                                       " error - {1}. Their telephone call is costing "
-                                       "{3,number,currency}."; // number,currency
+                patternToCheck=        u"0:Someone from {2} is receiving a #{0}"
+                                        " error - {1}. Their telephone call is costing "
+                                        "{3,number,currency}."; // number,currency
                 messageLocale=                      Locale("en","US");
                 countryToCheck=                     Locale("","HR");
                 currencyToCheck=                    8192.77;
-                expected=  "0:Someone from Croatia is receiving a #4 error - "
+                expected=  u"0:Someone from Croatia is receiving a #4 error - "
                             "U_FILE_ACCESS_ERROR. Their telephone call is costing $8,192.77.";
                 break;
             case 1:
                 statusToCheck=                      U_INDEX_OUTOFBOUNDS_ERROR;
-                patternToCheck=                     "1:A customer in {2} is receiving a #{0} error - {1}. "
-                                                    "Their telephone call is costing {3,number,currency}."; // number,currency
+                patternToCheck=                     u"1:A customer in {2} is receiving a #{0} error - {1}. "
+                                                     "Their telephone call is costing {3,number,currency}."; // number,currency
                 messageLocale=                      Locale("de","DE@currency=DEM");
                 countryToCheck=                     Locale("","BF");
                 currencyToCheck=                    2.32;
-                expected=                           CharsToUnicodeString(
-                                                    "1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. "
-                                                    "Their telephone call is costing 2,32\\u00A0DM.");
+                expected=                           u"1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. "
+                                                    u"Their telephone call is costing 2,32\u00A0DM.";
                 break;
             case 2:
                 statusToCheck=                      U_MEMORY_ALLOCATION_ERROR;
-                patternToCheck=   "2:user in {2} is receiving a #{0} error - {1}. "
+                patternToCheck=   u"2:user in {2} is receiving a #{0} error - {1}. "
                                   "They insist they just spent {3,number,currency} "
                                   "on memory."; // number,currency
                 messageLocale=                      Locale("de","AT@currency=ATS"); // Austrian German
                 countryToCheck=                     Locale("","US"); // hmm
                 currencyToCheck=                    40193.12;
-                expected=       CharsToUnicodeString(
-                            "2:user in Vereinigte Staaten is receiving a #7 error"
-                            " - U_MEMORY_ALLOCATION_ERROR. They insist they just spent"
-                            " \\u00f6S\\u00A040.193,12 on memory.");
+                expected=       u"2:user in Vereinigte Staaten is receiving a #7 error"
+                                u" - U_MEMORY_ALLOCATION_ERROR. They insist they just spent"
+                                u" \u00f6S\u00A040.193,12 on memory.";
                 break;
             }
 
@@ -790,14 +627,14 @@ public:
             if(U_FAILURE(status))
             {
                 UnicodeString tmp(u_errorName(status));
-                IntlTest::gTest->errln("Failure on message format, pattern=" + patternToCheck +
+                IntlTest::gTest->errln(u"Failure on message format, pattern=" + patternToCheck +
                         ", error = " + tmp);
                 goto cleanupAndReturn;
             }
 
             if(result != expected)
             {
-                IntlTest::gTest->errln("PatternFormat: \n" + showDifference(expected,result));
+                IntlTest::gTest->errln(u"PatternFormat: \n" + showDifference(expected,result));
                 goto cleanupAndReturn;
             }
             // test the Thread Safe Format
@@ -827,7 +664,7 @@ void MultithreadTest::TestThreadedIntl()
     UErrorCode threadSafeErr = U_ZERO_ERROR;
 
     ThreadSafeFormatSharedData sharedData(threadSafeErr);
-    assertSuccess("initializing ThreadSafeFormat", threadSafeErr, TRUE);
+    assertSuccess(WHERE, threadSafeErr, TRUE);
 
     //
     //  Create and start the test threads
@@ -873,17 +710,6 @@ struct Line {
     int32_t buflen;
 } ;
 
-static UBool
-skipLineBecauseOfBug(const UChar *s, int32_t length) {
-    // TODO: Fix ICU ticket #8052
-    if(length >= 3 &&
-            (s[0] == 0xfb2 || s[0] == 0xfb3) &&
-            s[1] == 0x334 &&
-            (s[2] == 0xf73 || s[2] == 0xf75 || s[2] == 0xf81)) {
-        return TRUE;
-    }
-    return FALSE;
-}
 
 static UCollationResult
 normalizeResult(int32_t result) {
@@ -904,7 +730,7 @@ public:
         noLines(0),
         isAtLeastUCA62(TRUE)
     {
-    };
+    }
     void setCollator(Collator *c, Line *l, int32_t nl, UBool atLeastUCA62)
     {
         coll = c;
@@ -922,8 +748,6 @@ public:
         for(i = 0; i < noLines; i++) {
             if(lines[i].buflen == 0) { continue; }
 
-            if(skipLineBecauseOfBug(lines[i].buff, lines[i].buflen)) { continue; }
-
             int32_t resLen = coll->getSortKey(lines[i].buff, lines[i].buflen, newSk, 1024);
 
             if(oldSk != NULL) {
@@ -932,12 +756,12 @@ public:
                 int32_t cmpres2 = coll->compare(lines[i].buff, lines[i].buflen, lines[prev].buff, lines[prev].buflen);
 
                 if(cmpres != -cmpres2) {
-                    IntlTest::gTest->errln(UnicodeString("Compare result not symmetrical on line ") + (i + 1));
+                    IntlTest::gTest->errln(UnicodeString(u"Compare result not symmetrical on line ") + (i + 1));
                     break;
                 }
 
                 if(cmpres != normalizeResult(skres)) {
-                    IntlTest::gTest->errln(UnicodeString("Difference between coll->compare and sortkey compare on line ") + (i + 1));
+                    IntlTest::gTest->errln(UnicodeString(u"Difference between coll->compare and sortkey compare on line ") + (i + 1));
                     break;
                 }
 
@@ -951,7 +775,7 @@ public:
                     // which we do via setting strength=identical.
                 }
                 if(res > 0) {
-                    IntlTest::gTest->errln(UnicodeString("Line is not greater or equal than previous line, for line ") + (i + 1));
+                    IntlTest::gTest->errln(UnicodeString(u"Line is not greater or equal than previous line, for line ") + (i + 1));
                     break;
                 }
             }
@@ -1070,7 +894,7 @@ void MultithreadTest::TestCollators()
     int32_t spawnResult = 0;
     LocalArray<CollatorThreadTest> tests(new CollatorThreadTest[kCollatorThreadThreads]);
 
-    logln(UnicodeString("Spawning: ") + kCollatorThreadThreads + " threads * " + kFormatThreadIterations + " iterations each.");
+    logln(UnicodeString(u"Spawning: ") + kCollatorThreadThreads + u" threads * " + kFormatThreadIterations + u" iterations each.");
     int32_t j = 0;
     for(j = 0; j < kCollatorThreadThreads; j++) {
         //logln("Setting collator %i", j);
@@ -1118,7 +942,7 @@ public:
         : SimpleThread(),
         fTraceInfo(0)
     {
-    };
+    }
 
 
     virtual void run()
@@ -1127,12 +951,12 @@ public:
         int loopCount = 0;
 
         for (loopCount = 0; loopCount < kStringThreadIterations; loopCount++) {
-            if (*gSharedString != "This is the original test string.") {
+            if (*gSharedString != u"This is the original test string.") {
                 IntlTest::gTest->errln("%s:%d Original string is corrupt.", __FILE__, __LINE__);
                 break;
             }
             UnicodeString s1 = *gSharedString;
-            s1 += "cat this";
+            s1 += u"cat this";
             UnicodeString s2(s1);
             UnicodeString s3 = *gSharedString;
             s2 = s3;
@@ -1153,10 +977,10 @@ const UnicodeString *StringThreadTest2::gSharedString = NULL;
 void MultithreadTest::TestString()
 {
     int     j;
-    StringThreadTest2::gSharedString = new UnicodeString("This is the original test string.");
+    StringThreadTest2::gSharedString = new UnicodeString(u"This is the original test string.");
     StringThreadTest2  tests[kStringThreadThreads];
 
-    logln(UnicodeString("Spawning: ") + kStringThreadThreads + " threads * " + kStringThreadIterations + " iterations each.");
+    logln(UnicodeString(u"Spawning: ") + kStringThreadThreads + u" threads * " + kStringThreadIterations + u" iterations each.");
     for(j = 0; j < kStringThreadThreads; j++) {
         int32_t threadStatus = tests[j].start();
         if (threadStatus != 0) {
@@ -1169,7 +993,7 @@ void MultithreadTest::TestString()
 
     for(j=0; j<kStringThreadThreads; j++) {
         tests[j].join();
-        logln(UnicodeString("Test #") + j + " is complete.. ");
+        logln(UnicodeString(u"Test #") + j + u" is complete.. ");
     }
 
     delete StringThreadTest2::gSharedString;
@@ -1188,20 +1012,16 @@ void MultithreadTest::TestString()
 Transliterator *gSharedTranslit = NULL;
 class TxThread: public SimpleThread {
   public:
-    TxThread() {};
+    TxThread() {}
     ~TxThread();
     void run();
 };
 
 TxThread::~TxThread() {}
 void TxThread::run() {
-    UnicodeString greekString("\\u03B4\\u03B9\\u03B1\\u03C6\\u03BF\\u03C1\\u03B5\\u03C4\\u03B9\\u03BA\\u03BF\\u03CD\\u03C2");
-    greekString = greekString.unescape();
+    UnicodeString greekString(u"διαφορετικούς");
     gSharedTranslit->transliterate(greekString);
-    if (greekString[0] != 0x64)      // 'd'. The whole transliterated string is "diaphoretikous" (accented u).
-    {
-        IntlTest::gTest->errln("%s:%d Transliteration failed.", __FILE__, __LINE__);
-    }
+    IntlTest::gTest->assertEquals(WHERE, UnicodeString(u"diaphoretikoús"), greekString);
 }
 #endif
 
@@ -1210,10 +1030,8 @@ void MultithreadTest::TestAnyTranslit() {
 #if !UCONFIG_NO_TRANSLITERATION
     UErrorCode status = U_ZERO_ERROR;
     LocalPointer<Transliterator> tx(Transliterator::createInstance("Any-Latin", UTRANS_FORWARD, status));
-    if (U_FAILURE(status)) {
-        dataerrln("File %s, Line %d: Error, status = %s", __FILE__, __LINE__, u_errorName(status));
-        return;
-    }
+    if (!assertSuccess(WHERE, status, true)) { return; }
+
     gSharedTranslit = tx.getAlias();
     TxThread  threads[4];
     int32_t i;
@@ -1229,93 +1047,6 @@ void MultithreadTest::TestAnyTranslit() {
 }
 
 
-//
-// Condition Variables Test
-//   Create a swarm of threads.
-//   Using a mutex and a condition variables each thread
-//     Increments a global count of started threads.
-//     Broadcasts that it has started.
-//     Waits on the condition that all threads have started.
-//     Increments a global count of finished threads.
-//     Waits on the condition that all threads have finished.
-//     Exits.
-//
-
-class CondThread: public SimpleThread {
-  public:
-    CondThread() :fFinished(false)  {};
-    ~CondThread() {};
-    void run();
-    bool  fFinished;
-};
-
-static UMutex gCTMutex = U_MUTEX_INITIALIZER;
-static UConditionVar gCTConditionVar = U_CONDITION_INITIALIZER;
-int gConditionTestOne = 1;   // Value one. Non-const, extern linkage to inhibit
-                             //   compiler assuming a known value.
-int gStartedThreads;
-int gFinishedThreads;
-static const int NUMTHREADS = 10;
-
-
-// Worker thread function.
-void CondThread::run() {
-    umtx_lock(&gCTMutex);
-    gStartedThreads += gConditionTestOne;
-    umtx_condBroadcast(&gCTConditionVar);
-
-    while (gStartedThreads < NUMTHREADS) {
-        if (gFinishedThreads != 0) {
-            IntlTest::gTest->errln("File %s, Line %d: Error, gStartedThreads = %d, gFinishedThreads = %d",
-                             __FILE__, __LINE__, gStartedThreads, gFinishedThreads);
-        }
-        umtx_condWait(&gCTConditionVar, &gCTMutex);
-    }
-
-    gFinishedThreads += gConditionTestOne;
-    fFinished = true;
-    umtx_condBroadcast(&gCTConditionVar);
-
-    while (gFinishedThreads < NUMTHREADS) {
-        umtx_condWait(&gCTConditionVar, &gCTMutex);
-    }
-    umtx_unlock(&gCTMutex);
-}
-
-void MultithreadTest::TestConditionVariables() {
-    gStartedThreads = 0;
-    gFinishedThreads = 0;
-    int i;
-
-    umtx_lock(&gCTMutex);
-    CondThread *threads[NUMTHREADS];
-    for (i=0; i<NUMTHREADS; ++i) {
-        threads[i] = new CondThread;
-        threads[i]->start();
-    }
-
-    while (gStartedThreads < NUMTHREADS) {
-        umtx_condWait(&gCTConditionVar, &gCTMutex);
-    }
-
-    while (gFinishedThreads < NUMTHREADS) {
-        umtx_condWait(&gCTConditionVar, &gCTMutex);
-    }
-
-    umtx_unlock(&gCTMutex);
-
-    for (i=0; i<NUMTHREADS; ++i) {
-        if (!threads[i]->fFinished) {
-            errln("File %s, Line %d: Error, threads[%d]->fFinished == false", __FILE__, __LINE__, i);
-        }
-    }
-
-    for (i=0; i<NUMTHREADS; ++i) {
-        threads[i]->join();
-        delete threads[i];
-    }
-}
-
 
 //
 // Unified Cache Test
@@ -1350,6 +1081,9 @@ class UCTMultiThreadItem : public SharedObject {
 
 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 {
@@ -1365,21 +1099,23 @@ const UCTMultiThreadItem *LocaleCacheKey<UCTMultiThreadItem>::createObject(
         return result;
     }
 
-    umtx_lock(&gCTMutex);
-    bool firstObject = (gObjectsCreated == 0);
-    if (firstObject) {
-        // Force the first object creation that comes through to wait
-        // until other have completed. Verifies that cache doesn't
-        // deadlock when a creation is slow.
-
-        // Note that gObjectsCreated needs to be incremeneted from 0 to 1
-        // early, to keep subsequent threads from entering this path.
-        gObjectsCreated = 1;
-        while (gObjectsCreated < 3) {
-            umtx_condWait(&gCTConditionVar, &gCTMutex);
+    bool firstObject = false;
+    {
+        std::unique_lock<std::mutex> lock(*gCTMutex);
+        firstObject = (gObjectsCreated == 0);
+        if (firstObject) {
+            // Force the first object creation that comes through to wait
+            // until other have completed. Verifies that cache doesn't
+            // deadlock when a creation is slow.
+
+            // Note that gObjectsCreated needs to be incremeneted from 0 to 1
+            // early, to keep subsequent threads from entering this path.
+            gObjectsCreated = 1;
+            while (gObjectsCreated < 3) {
+                gCTConditionVar->wait(lock);
+            }
         }
     }
-    umtx_unlock(&gCTMutex);
 
     const UCTMultiThreadItem *result =
         new UCTMultiThreadItem(fLoc.getLanguage());
@@ -1388,15 +1124,16 @@ const UCTMultiThreadItem *LocaleCacheKey<UCTMultiThreadItem>::createObject(
     } else {
         result->addRef();
     }
-    
     // Log that we created an object. The first object was already counted,
     //    don't do it again.
-    umtx_lock(&gCTMutex);
-    if (!firstObject) {
-        gObjectsCreated += 1;
+    {
+        std::unique_lock<std::mutex> lock(*gCTMutex);
+        if (!firstObject) {
+            gObjectsCreated += 1;
+        }
+        gCTConditionVar->notify_all();
     }
-    umtx_condBroadcast(&gCTConditionVar);
-    umtx_unlock(&gCTMutex);
 
     return result;
 }
@@ -1408,8 +1145,8 @@ class UnifiedCacheThread: public SimpleThread {
     UnifiedCacheThread(
             const UnifiedCache *cache,
             const char *loc,
-            const char *loc2) : fCache(cache), fLoc(loc), fLoc2(loc2) {};
-    ~UnifiedCacheThread() {};
+            const char *loc2) : fCache(cache), fLoc(loc), fLoc2(loc2) {}
+    ~UnifiedCacheThread() {}
     void run();
     void exerciseByLocale(const Locale &);
     const UnifiedCache *fCache;
@@ -1423,12 +1160,7 @@ void UnifiedCacheThread::exerciseByLocale(const Locale &locale) {
     fCache->get(
             LocaleCacheKey<UCTMultiThreadItem>(locale), fCache, origItem, status);
     U_ASSERT(U_SUCCESS(status));
-    if (uprv_strcmp(locale.getLanguage(), origItem->value)) {
-      IntlTest::gTest->errln(
-              "%s:%d Expected %s, got %s", __FILE__, __LINE__,
-              locale.getLanguage(),
-              origItem->value);
-    }
+    IntlTest::gTest->assertEquals(WHERE, locale.getLanguage(), origItem->value);
 
     // Fetch the same item again many times. We should always get the same
     // pointer since this client is already holding onto it
@@ -1436,12 +1168,7 @@ void UnifiedCacheThread::exerciseByLocale(const Locale &locale) {
         const UCTMultiThreadItem *item = NULL;
         fCache->get(
                 LocaleCacheKey<UCTMultiThreadItem>(locale), fCache, item, status);
-        if (item != origItem) {
-            IntlTest::gTest->errln(
-                    "%s:%d Expected to get the same pointer",
-                     __FILE__,
-                     __LINE__);
-        }
+        IntlTest::gTest->assertTrue(WHERE, item == origItem);
         if (item != NULL) {
             item->removeRef();
         }
@@ -1451,7 +1178,7 @@ void UnifiedCacheThread::exerciseByLocale(const Locale &locale) {
 
 void UnifiedCacheThread::run() {
     // Run the exercise with 2 different locales so that we can exercise
-    // eviction more. If each thread exerices just one locale, then
+    // eviction more. If each thread exercises just one locale, then
     // eviction can't start until the threads end.
     exerciseByLocale(fLoc);
     exerciseByLocale(fLoc2);
@@ -1468,7 +1195,9 @@ void MultithreadTest::TestUnifiedCache() {
     cache.setEvictionPolicy(2, 0, status);
     U_ASSERT(U_SUCCESS(status));
 
-    gFinishedThreads = 0;
+    gCTMutex = new std::mutex();
+    gCTConditionVar = new std::condition_variable();
+
     gObjectsCreated = 0;
 
     UnifiedCacheThread *threads[CACHE_LOAD][UPRV_LENGTHOF(gCacheLocales)];
@@ -1502,7 +1231,7 @@ void MultithreadTest::TestUnifiedCache() {
 
     }
 
-    assertEquals("unused values", 2, cache.unusedCount());
+    assertEquals(WHERE, 2, cache.unusedCount());
 
     // clean up threads
     for (int32_t i=0; i<CACHE_LOAD; ++i) {
@@ -1510,6 +1239,8 @@ void MultithreadTest::TestUnifiedCache() {
             delete threads[i][j];
         }
     }
+    delete gCTMutex;
+    delete gCTConditionVar;
 }
 
 #if !UCONFIG_NO_TRANSLITERATION
@@ -1524,8 +1255,8 @@ static const UnicodeString *gTranslitExpected;
 
 class BreakTranslitThread: public SimpleThread {
   public:
-    BreakTranslitThread() {};
-    ~BreakTranslitThread() {};
+    BreakTranslitThread() {}
+    ~BreakTranslitThread() {}
     void run();
 };
 
@@ -1543,16 +1274,16 @@ void BreakTranslitThread::run() {
 void MultithreadTest::TestBreakTranslit() {
     UErrorCode status = U_ZERO_ERROR;
     UnicodeString input(
-        "\\u0E42\\u0E14\\u0E22\\u0E1E\\u0E37\\u0E49\\u0E19\\u0E10\\u0E32\\u0E19\\u0E41\\u0E25\\u0E49\\u0E27,");
-    input = input.unescape();
+        u"\u0E42\u0E14\u0E22\u0E1E\u0E37\u0E49\u0E19\u0E10\u0E32\u0E19\u0E41\u0E25\u0E49\u0E27,");
+        // Thai script, โดยพื้นฐานแล้ว
     gTranslitInput = &input;
 
     gSharedTransliterator = Transliterator::createInstance(
-        UNICODE_STRING_SIMPLE("Any-Latin; Lower; NFD; [:Diacritic:]Remove; NFC; Latin-ASCII;"), UTRANS_FORWARD, status);
-    if (!gSharedTransliterator) {
-         return;
-     }
-    TSMTHREAD_ASSERT_SUCCESS(status); 
+        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);
@@ -1571,4 +1302,67 @@ void MultithreadTest::TestBreakTranslit() {
     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.
+}
+#endif /* !UCONFIG_NO_FORMATTING */
+
 #endif /* !UCONFIG_NO_TRANSLITERATION */