• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
4  * COPYRIGHT:
5  * Copyright (c) 1999-2015, International Business Machines Corporation and
6  * others. All Rights Reserved.
7  ********************************************************************/
8 
9 #include "simplethread.h"
10 
11 #include "unicode/utypes.h"
12 #include "unicode/ustring.h"
13 #include "umutex.h"
14 #include "cmemory.h"
15 #include "cstring.h"
16 #include "indiancal.h"
17 #include "uparse.h"
18 #include "unicode/localpointer.h"
19 #include "unicode/resbund.h"
20 #include "unicode/udata.h"
21 #include "unicode/uloc.h"
22 #include "unicode/locid.h"
23 #include "putilimp.h"
24 #include "intltest.h"
25 #include "tsmthred.h"
26 #include "unicode/ushape.h"
27 #include "unicode/translit.h"
28 #include "sharedobject.h"
29 #include "unifiedcache.h"
30 #include "uassert.h"
31 
32 
MultithreadTest()33 MultithreadTest::MultithreadTest()
34 {
35 }
36 
~MultithreadTest()37 MultithreadTest::~MultithreadTest()
38 {
39 }
40 
41 #include <stdio.h>
42 #include <string.h>
43 #include <ctype.h>    // tolower, toupper
44 #include <memory>
45 
46 #include "unicode/putil.h"
47 
48 // for mthreadtest
49 #include "unicode/numfmt.h"
50 #include "unicode/choicfmt.h"
51 #include "unicode/msgfmt.h"
52 #include "unicode/locid.h"
53 #include "unicode/coll.h"
54 #include "unicode/calendar.h"
55 #include "ucaconf.h"
56 
57 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)58 void MultithreadTest::runIndexedTest( int32_t index, UBool exec,
59                 const char* &name, char* /*par*/ ) {
60     if (exec)
61         logln("TestSuite MultithreadTest: ");
62 
63     TESTCASE_AUTO_BEGIN;
64     TESTCASE_AUTO(TestThreads);
65 #if !UCONFIG_NO_FORMATTING
66     TESTCASE_AUTO(TestThreadedIntl);
67 #endif
68 #if !UCONFIG_NO_COLLATION
69     TESTCASE_AUTO(TestCollators);
70 #endif /* #if !UCONFIG_NO_COLLATION */
71     TESTCASE_AUTO(TestString);
72     TESTCASE_AUTO(TestArabicShapingThreads);
73     TESTCASE_AUTO(TestAnyTranslit);
74     TESTCASE_AUTO(TestUnifiedCache);
75 #if !UCONFIG_NO_TRANSLITERATION
76     TESTCASE_AUTO(TestBreakTranslit);
77     TESTCASE_AUTO(TestIncDec);
78 #if !UCONFIG_NO_FORMATTING
79     TESTCASE_AUTO(Test20104);
80 #endif /* #if !UCONFIG_NO_FORMATTING */
81 #endif /* #if !UCONFIG_NO_TRANSLITERATION */
82     TESTCASE_AUTO_END;
83 }
84 
85 
86 //-----------------------------------------------------------------------------------
87 //
88 //   TestThreads -- see if threads really work at all.
89 //
90 //   Set up N threads pointing at N chars. When they are started, they will
91 //   set their chars. At the end we make sure they are all set.
92 //
93 //-----------------------------------------------------------------------------------
94 
95 class TestThreadsThread : public SimpleThread
96 {
97 public:
TestThreadsThread(char * whatToChange)98     TestThreadsThread(char* whatToChange) { fWhatToChange = whatToChange; }
run()99     virtual void run() override {
100         Mutex m;
101         *fWhatToChange = '*';
102     }
103 private:
104     char *fWhatToChange;
105 };
106 
107 
TestThreads()108 void MultithreadTest::TestThreads()
109 {
110     static const int32_t THREADTEST_NRTHREADS = 8;
111     char threadTestChars[THREADTEST_NRTHREADS + 1];
112     SimpleThread *threads[THREADTEST_NRTHREADS];
113     int32_t numThreadsStarted = 0;
114 
115     int32_t i;
116     for(i=0;i<THREADTEST_NRTHREADS;i++)
117     {
118         threadTestChars[i] = ' ';
119         threads[i] = new TestThreadsThread(&threadTestChars[i]);
120     }
121     threadTestChars[THREADTEST_NRTHREADS] = '\0';
122 
123     logln("->" + UnicodeString(threadTestChars) + "<- Firing off threads.. ");
124     for(i=0;i<THREADTEST_NRTHREADS;i++)
125     {
126         if (threads[i]->start() != 0) {
127             errln("Error starting thread %d", i);
128         }
129         else {
130             numThreadsStarted++;
131         }
132         logln(" Subthread started.");
133     }
134 
135     assertTrue(WHERE, THREADTEST_NRTHREADS == numThreadsStarted);
136 
137     logln("Waiting for threads to be set..");
138     for(i=0; i<THREADTEST_NRTHREADS; i++) {
139         threads[i]->join();
140         if (threadTestChars[i] != '*') {
141             errln("%s:%d Thread %d failed.", __FILE__, __LINE__, i);
142         }
143         delete threads[i];
144     }
145 }
146 
147 
148 //-----------------------------------------------------------------------------------
149 //
150 //   TestArabicShapeThreads -- see if calls to u_shapeArabic in many threads works successfully
151 //
152 //   Set up N threads pointing at N chars. When they are started, they will make calls to doTailTest which tests
153 //   u_shapeArabic, if the calls are successful it will the set * chars.
154 //   At the end we make sure all threads managed to run u_shapeArabic successfully.
155 //   This is a unit test for ticket 9473
156 //
157 //-----------------------------------------------------------------------------------
158 
159 class TestArabicShapeThreads : public SimpleThread
160 {
161 public:
TestArabicShapeThreads()162     TestArabicShapeThreads() {}
run()163     virtual void run() override { doTailTest(); }
164 private:
165 	void doTailTest();
166 };
167 
168 
doTailTest(void)169 void TestArabicShapeThreads::doTailTest(void) {
170     static const UChar src[] = { 0x0020, 0x0633, 0 };
171     static const UChar dst_old[] = { 0xFEB1, 0x200B,0 };
172     static const UChar dst_new[] = { 0xFEB1, 0xFE73,0 };
173     UChar dst[3] = { 0x0000, 0x0000,0 };
174     int32_t length;
175     UErrorCode status;
176 
177     for (int32_t loopCount = 0; loopCount < 100; loopCount++) {
178         status = U_ZERO_ERROR;
179         length = u_shapeArabic(src, -1, dst, UPRV_LENGTHOF(dst),
180                 U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR, &status);
181         if(U_FAILURE(status)) {
182             IntlTest::gTest->errln("Fail: status %s\n", u_errorName(status));
183             return;
184         } else if(length!=2) {
185             IntlTest::gTest->errln("Fail: len %d expected 3\n", length);
186             return;
187         } else if(u_strncmp(dst,dst_old,UPRV_LENGTHOF(dst))) {
188             IntlTest::gTest->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
189                     dst[0],dst[1],dst_old[0],dst_old[1]);
190             return;
191         }
192 
193 
194         // Trying new tail
195         status = U_ZERO_ERROR;
196         length = u_shapeArabic(src, -1, dst, UPRV_LENGTHOF(dst),
197                 U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR|U_SHAPE_TAIL_NEW_UNICODE, &status);
198         if(U_FAILURE(status)) {
199             IntlTest::gTest->errln("Fail: status %s\n", u_errorName(status));
200             return;
201         } else if(length!=2) {
202             IntlTest::gTest->errln("Fail: len %d expected 3\n", length);
203             return;
204         } else if(u_strncmp(dst,dst_new,UPRV_LENGTHOF(dst))) {
205             IntlTest::gTest->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
206                     dst[0],dst[1],dst_new[0],dst_new[1]);
207             return;
208         }
209     }
210     return;
211 }
212 
213 
TestArabicShapingThreads()214 void MultithreadTest::TestArabicShapingThreads()
215 {
216     TestArabicShapeThreads threads[30];
217 
218     int32_t i;
219 
220     logln("-> do TestArabicShapingThreads <- Firing off threads.. ");
221     for(i=0; i < UPRV_LENGTHOF(threads); i++) {
222         if (threads[i].start() != 0) {
223             errln("Error starting thread %d", i);
224         }
225     }
226 
227     for(i=0; i < UPRV_LENGTHOF(threads); i++) {
228         threads[i].join();
229     }
230     logln("->TestArabicShapingThreads <- Got all threads! cya");
231 }
232 
233 
234 //-------------------------------------------------------------------------------------------
235 //
236 //   TestMultithreadedIntl.  Test ICU Formatting in a multi-threaded environment
237 //
238 //-------------------------------------------------------------------------------------------
239 
240 
241 // * Show exactly where the string's differences lie.
showDifference(const UnicodeString & expected,const UnicodeString & result)242 UnicodeString showDifference(const UnicodeString& expected, const UnicodeString& result)
243 {
244     UnicodeString res;
245     res = expected + u"<Expected\n";
246     if(expected.length() != result.length())
247         res += u" [ Different lengths ] \n";
248     else
249     {
250         for(int32_t i=0;i<expected.length();i++)
251         {
252             if(expected[i] == result[i])
253             {
254                 res += u" ";
255             }
256             else
257             {
258                 res += u"|";
259             }
260         }
261         res += u"<Differences";
262         res += u"\n";
263     }
264     res += result + u"<Result\n";
265 
266     return res;
267 }
268 
269 
270 //-------------------------------------------------------------------------------------------
271 //
272 //   FormatThreadTest - a thread that tests performing a number of numberformats.
273 //
274 //-------------------------------------------------------------------------------------------
275 
276 const int kFormatThreadIterations = 100;  // # of iterations per thread
277 const int kFormatThreadThreads    = 10;  // # of threads to spawn
278 
279 #if !UCONFIG_NO_FORMATTING
280 
281 
282 
283 struct FormatThreadTestData
284 {
285     double number;
286     UnicodeString string;
FormatThreadTestDataFormatThreadTestData287     FormatThreadTestData(double a, const UnicodeString& b) : number(a),string(b) {}
288 } ;
289 
290 
291 // "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3 number,currency}."
292 
formatErrorMessage(UErrorCode & realStatus,const UnicodeString & pattern,const Locale & theLocale,UErrorCode inStatus0,const Locale & inCountry2,double currency3,UnicodeString & result)293 static void formatErrorMessage(UErrorCode &realStatus, const UnicodeString& pattern, const Locale& theLocale,
294                      UErrorCode inStatus0,                       // statusString 1
295                      const Locale &inCountry2, double currency3, // these numbers are the message arguments.
296                      UnicodeString &result)
297 {
298     if(U_FAILURE(realStatus))
299         return; // you messed up
300 
301     UnicodeString errString1(u_errorName(inStatus0));
302 
303     UnicodeString countryName2;
304     inCountry2.getDisplayCountry(theLocale,countryName2);
305 
306     Formattable myArgs[] = {
307         Formattable((int32_t)inStatus0),   // inStatus0      {0}
308         Formattable(errString1), // statusString1 {1}
309         Formattable(countryName2),  // inCountry2 {2}
310         Formattable(currency3)// currency3  {3,number,currency}
311     };
312 
313     LocalPointer<MessageFormat> fmt(new MessageFormat(u"MessageFormat's API is broken!!!!!!!!!!!",realStatus), realStatus);
314     if (U_FAILURE(realStatus)) {
315         return;
316     }
317     fmt->setLocale(theLocale);
318     fmt->applyPattern(pattern, realStatus);
319 
320     FieldPosition ignore = 0;
321     fmt->format(myArgs,4,result,ignore,realStatus);
322 }
323 
324 /**
325   * Shared formatters &  data used by instances of ThreadSafeFormat.
326   * Exactly one instance of this class is created, and it is then shared concurrently
327   * by the multiple instances of ThreadSafeFormat.
328   */
329 class ThreadSafeFormatSharedData {
330   public:
331     ThreadSafeFormatSharedData(UErrorCode &status);
332     ~ThreadSafeFormatSharedData();
333     LocalPointer<NumberFormat>  fFormat;
334     Formattable    fYDDThing;
335     Formattable    fBBDThing;
336     UnicodeString  fYDDStr;
337     UnicodeString  fBBDStr;
338 };
339 
340 const ThreadSafeFormatSharedData *gSharedData = NULL;
341 
ThreadSafeFormatSharedData(UErrorCode & status)342 ThreadSafeFormatSharedData::ThreadSafeFormatSharedData(UErrorCode &status) {
343     fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status));
344     static const UChar *kYDD = u"YDD";
345     static const UChar *kBBD = u"BBD";
346     fYDDThing.adoptObject(new CurrencyAmount(123.456, kYDD, status));
347     fBBDThing.adoptObject(new CurrencyAmount(987.654, kBBD, status));
348     if (U_FAILURE(status)) {
349         return;
350     }
351     fFormat->format(fYDDThing, fYDDStr, NULL, status);
352     fFormat->format(fBBDThing, fBBDStr, NULL, status);
353     gSharedData = this;
354 }
355 
~ThreadSafeFormatSharedData()356 ThreadSafeFormatSharedData::~ThreadSafeFormatSharedData() {
357     gSharedData = NULL;
358 }
359 
360 /**
361  * Class for thread-safe testing of format.
362  *   Instances of this class appear as members of class FormatThreadTest.
363  *   Multiple instances of FormatThreadTest coexist.
364  *   ThreadSafeFormat::doStuff() is called concurrently to test the thread safety of
365  *   various shared format operations.
366  */
367 class ThreadSafeFormat {
368 public:
369   /* give a unique offset to each thread */
370   ThreadSafeFormat(UErrorCode &status);
371   UBool doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const;
372 private:
373   LocalPointer<NumberFormat> fFormat; // formatter - en_US constructed currency
374 };
375 
376 
ThreadSafeFormat(UErrorCode & status)377 ThreadSafeFormat::ThreadSafeFormat(UErrorCode &status) {
378   fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status));
379 }
380 
381 static const UChar *kUSD = u"USD";
382 
doStuff(int32_t offset,UnicodeString & appendErr,UErrorCode & status) const383 UBool ThreadSafeFormat::doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const {
384   UBool okay = true;
385 
386   if(u_strcmp(fFormat->getCurrency(), kUSD)) {
387     appendErr.append(u"fFormat currency != ")
388       .append(kUSD)
389       .append(u", =")
390       .append(fFormat->getCurrency())
391       .append(u"! ");
392     okay = false;
393   }
394 
395   if(u_strcmp(gSharedData->fFormat->getCurrency(), kUSD)) {
396     appendErr.append(u"gFormat currency != ")
397       .append(kUSD)
398       .append(u", =")
399       .append(gSharedData->fFormat->getCurrency())
400       .append(u"! ");
401     okay = false;
402   }
403   UnicodeString str;
404   const UnicodeString *o=NULL;
405   Formattable f;
406   const NumberFormat *nf = NULL; // only operate on it as const.
407   switch(offset%4) {
408   case 0:  f = gSharedData->fYDDThing;  o = &gSharedData->fYDDStr;  nf = gSharedData->fFormat.getAlias();  break;
409   case 1:  f = gSharedData->fBBDThing;  o = &gSharedData->fBBDStr;  nf = gSharedData->fFormat.getAlias();  break;
410   case 2:  f = gSharedData->fYDDThing;  o = &gSharedData->fYDDStr;  nf = fFormat.getAlias();  break;
411   case 3:  f = gSharedData->fBBDThing;  o = &gSharedData->fBBDStr;  nf = fFormat.getAlias();  break;
412   }
413   nf->format(f, str, NULL, status);
414 
415   if(*o != str) {
416     appendErr.append(showDifference(*o, str));
417     okay = false;
418   }
419   return okay;
420 }
421 
isAcceptable(void *,const char *,const char *,const UDataInfo *)422 UBool U_CALLCONV isAcceptable(void *, const char *, const char *, const UDataInfo *) {
423     return true;
424 }
425 
426 //static UMTX debugMutex = NULL;
427 //static UMTX gDebugMutex;
428 
429 
430 class FormatThreadTest : public SimpleThread
431 {
432 public:
433     int     fNum;
434     int     fTraceInfo;
435 
436     LocalPointer<ThreadSafeFormat> fTSF;
437 
FormatThreadTest()438     FormatThreadTest() // constructor is NOT multithread safe.
439         : SimpleThread(),
440         fNum(0),
441         fTraceInfo(0),
442         fTSF(NULL),
443         fOffset(0)
444         // the locale to use
445     {
446         UErrorCode status = U_ZERO_ERROR;      // TODO: rearrange code to allow checking of status.
447         fTSF.adoptInstead(new ThreadSafeFormat(status));
448         static int32_t fgOffset = 0;
449         fgOffset += 3;
450         fOffset = fgOffset;
451     }
452 
453 
run()454     virtual void run() override
455     {
456         fTraceInfo                     = 1;
457         LocalPointer<NumberFormat> percentFormatter;
458         UErrorCode status = U_ZERO_ERROR;
459 
460 #if 0
461         // debugging code,
462         for (int i=0; i<4000; i++) {
463             status = U_ZERO_ERROR;
464             UDataMemory *data1 = udata_openChoice(0, "res", "en_US", isAcceptable, 0, &status);
465             UDataMemory *data2 = udata_openChoice(0, "res", "fr", isAcceptable, 0, &status);
466             udata_close(data1);
467             udata_close(data2);
468             if (U_FAILURE(status)) {
469                 error("udata_openChoice failed.\n");
470                 break;
471             }
472         }
473         return;
474 #endif
475 
476 #if 0
477         // debugging code,
478         int m;
479         for (m=0; m<4000; m++) {
480             status         = U_ZERO_ERROR;
481             UResourceBundle *res   = NULL;
482             const char *localeName = NULL;
483 
484             Locale  loc = Locale::getEnglish();
485 
486             localeName = loc.getName();
487             // localeName = "en";
488 
489             // ResourceBundle bund = ResourceBundle(0, loc, status);
490             //umtx_lock(&gDebugMutex);
491             res = ures_open(NULL, localeName, &status);
492             //umtx_unlock(&gDebugMutex);
493 
494             //umtx_lock(&gDebugMutex);
495             ures_close(res);
496             //umtx_unlock(&gDebugMutex);
497 
498             if (U_FAILURE(status)) {
499                 error("Resource bundle construction failed.\n");
500                 break;
501             }
502         }
503         return;
504 #endif
505 
506         // Keep this data here to avoid static initialization.
507         FormatThreadTestData kNumberFormatTestData[] =
508         {
509             FormatThreadTestData((double)5.0, UnicodeString(u"5")),
510                 FormatThreadTestData( 6.0, UnicodeString(u"6")),
511                 FormatThreadTestData( 20.0, UnicodeString(u"20")),
512                 FormatThreadTestData( 8.0, UnicodeString(u"8")),
513                 FormatThreadTestData( 8.3, UnicodeString(u"8.3")),
514                 FormatThreadTestData( 12345, UnicodeString(u"12,345")),
515                 FormatThreadTestData( 81890.23, UnicodeString(u"81,890.23")),
516         };
517         int32_t kNumberFormatTestDataLength = UPRV_LENGTHOF(kNumberFormatTestData);
518 
519         // Keep this data here to avoid static initialization.
520         FormatThreadTestData kPercentFormatTestData[] =
521         {
522             FormatThreadTestData((double)5.0, CharsToUnicodeString("500\\u00a0%")),
523                 FormatThreadTestData( 1.0, CharsToUnicodeString("100\\u00a0%")),
524                 FormatThreadTestData( 0.26, CharsToUnicodeString("26\\u00a0%")),
525                 FormatThreadTestData(
526                    16384.99, CharsToUnicodeString("1\\u202F638\\u202F499\\u00a0%")), // U+202F = NNBSP
527                 FormatThreadTestData(
528                     81890.23, CharsToUnicodeString("8\\u202F189\\u202F023\\u00a0%")),
529         };
530         int32_t kPercentFormatTestDataLength = UPRV_LENGTHOF(kPercentFormatTestData);
531         int32_t iteration;
532 
533         status = U_ZERO_ERROR;
534         LocalPointer<NumberFormat> formatter(NumberFormat::createInstance(Locale::getEnglish(),status));
535         if(U_FAILURE(status)) {
536             IntlTest::gTest->dataerrln("%s:%d Error %s on NumberFormat::createInstance().",
537                     __FILE__, __LINE__, u_errorName(status));
538             goto cleanupAndReturn;
539         }
540 
541         percentFormatter.adoptInstead(NumberFormat::createPercentInstance(Locale::getFrench(),status));
542         if(U_FAILURE(status))             {
543             IntlTest::gTest->errln("%s:%d Error %s on NumberFormat::createPercentInstance().",
544                     __FILE__, __LINE__, u_errorName(status));
545             goto cleanupAndReturn;
546         }
547 
548         for(iteration = 0;!IntlTest::gTest->getErrors() && iteration<kFormatThreadIterations;iteration++)
549         {
550 
551             int32_t whichLine = (iteration + fOffset)%kNumberFormatTestDataLength;
552 
553             UnicodeString  output;
554 
555             formatter->format(kNumberFormatTestData[whichLine].number, output);
556 
557             if(0 != output.compare(kNumberFormatTestData[whichLine].string)) {
558                 IntlTest::gTest->errln("format().. expected " + kNumberFormatTestData[whichLine].string
559                         + " got " + output);
560                 goto cleanupAndReturn;
561             }
562 
563             // Now check percent.
564             output.remove();
565             whichLine = (iteration + fOffset)%kPercentFormatTestDataLength;
566 
567             percentFormatter->format(kPercentFormatTestData[whichLine].number, output);
568             if(0 != output.compare(kPercentFormatTestData[whichLine].string))
569             {
570                 IntlTest::gTest->errln("percent format().. \n" +
571                         showDifference(kPercentFormatTestData[whichLine].string,output));
572                 goto cleanupAndReturn;
573             }
574 
575             // Test message error
576             const int       kNumberOfMessageTests = 3;
577             UErrorCode      statusToCheck;
578             UnicodeString   patternToCheck;
579             Locale          messageLocale;
580             Locale          countryToCheck;
581             double          currencyToCheck;
582 
583             UnicodeString   expected;
584 
585             // load the cases.
586             switch((iteration+fOffset) % kNumberOfMessageTests)
587             {
588             default:
589             case 0:
590                 statusToCheck=                      U_FILE_ACCESS_ERROR;
591                 patternToCheck=        u"0:Someone from {2} is receiving a #{0}"
592                                         " error - {1}. Their telephone call is costing "
593                                         "{3,number,currency}."; // number,currency
594                 messageLocale=                      Locale("en","US");
595                 countryToCheck=                     Locale("","HR");
596                 currencyToCheck=                    8192.77;
597                 expected=  u"0:Someone from Croatia is receiving a #4 error - "
598                             "U_FILE_ACCESS_ERROR. Their telephone call is costing $8,192.77.";
599                 break;
600             case 1:
601                 statusToCheck=                      U_INDEX_OUTOFBOUNDS_ERROR;
602                 patternToCheck=                     u"1:A customer in {2} is receiving a #{0} error - {1}. "
603                                                      "Their telephone call is costing {3,number,currency}."; // number,currency
604                 messageLocale=                      Locale("de","DE@currency=DEM");
605                 countryToCheck=                     Locale("","BF");
606                 currencyToCheck=                    2.32;
607                 expected=                           u"1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. "
608                                                     u"Their telephone call is costing 2,32\u00A0DM.";
609                 break;
610             case 2:
611                 statusToCheck=                      U_MEMORY_ALLOCATION_ERROR;
612                 patternToCheck=   u"2:user in {2} is receiving a #{0} error - {1}. "
613                                   "They insist they just spent {3,number,currency} "
614                                   "on memory."; // number,currency
615                 messageLocale=                      Locale("de","AT@currency=ATS"); // Austrian German
616                 countryToCheck=                     Locale("","US"); // hmm
617                 currencyToCheck=                    40193.12;
618                 expected=       u"2:user in Vereinigte Staaten is receiving a #7 error"
619                                 u" - U_MEMORY_ALLOCATION_ERROR. They insist they just spent"
620                                 u" \u00f6S\u00A040.193,12 on memory.";
621                 break;
622             }
623 
624             UnicodeString result;
625             UErrorCode status = U_ZERO_ERROR;
626             formatErrorMessage(status,patternToCheck,messageLocale,statusToCheck,
627                                 countryToCheck,currencyToCheck,result);
628             if(U_FAILURE(status))
629             {
630                 UnicodeString tmp(u_errorName(status));
631                 IntlTest::gTest->errln(u"Failure on message format, pattern=" + patternToCheck +
632                         ", error = " + tmp);
633                 goto cleanupAndReturn;
634             }
635 
636             if(result != expected)
637             {
638                 IntlTest::gTest->errln(u"PatternFormat: \n" + showDifference(expected,result));
639                 goto cleanupAndReturn;
640             }
641             // test the Thread Safe Format
642             UnicodeString appendErr;
643             if(!fTSF->doStuff(fNum, appendErr, status)) {
644               IntlTest::gTest->errln(appendErr);
645               goto cleanupAndReturn;
646             }
647         }   /*  end of for loop */
648 
649 
650 
651 cleanupAndReturn:
652         fTraceInfo = 2;
653     }
654 
655 private:
656     int32_t fOffset; // where we are testing from.
657 };
658 
659 // ** The actual test function.
660 
TestThreadedIntl()661 void MultithreadTest::TestThreadedIntl()
662 {
663     UnicodeString theErr;
664 
665     UErrorCode threadSafeErr = U_ZERO_ERROR;
666 
667     ThreadSafeFormatSharedData sharedData(threadSafeErr);
668     assertSuccess(WHERE, threadSafeErr, true);
669 
670     //
671     //  Create and start the test threads
672     //
673     logln("Spawning: %d threads * %d iterations each.",
674                 kFormatThreadThreads, kFormatThreadIterations);
675     FormatThreadTest tests[kFormatThreadThreads];
676     int32_t j;
677     for(j = 0; j < UPRV_LENGTHOF(tests); j++) {
678         tests[j].fNum = j;
679         int32_t threadStatus = tests[j].start();
680         if (threadStatus != 0) {
681             errln("%s:%d System Error %d starting thread number %d.",
682                     __FILE__, __LINE__, threadStatus, j);
683             return;
684         }
685     }
686 
687 
688     for (j=0; j<UPRV_LENGTHOF(tests); j++) {
689         tests[j].join();
690         logln("Thread # %d is complete..", j);
691     }
692 }
693 #endif /* #if !UCONFIG_NO_FORMATTING */
694 
695 
696 
697 
698 
699 //-------------------------------------------------------------------------------------------
700 //
701 // Collation threading test
702 //
703 //-------------------------------------------------------------------------------------------
704 #if !UCONFIG_NO_COLLATION
705 
706 #define kCollatorThreadThreads   10  // # of threads to spawn
707 #define kCollatorThreadPatience kCollatorThreadThreads*30
708 
709 struct Line {
710     UChar buff[25];
711     int32_t buflen;
712 } ;
713 
714 
715 static UCollationResult
normalizeResult(int32_t result)716 normalizeResult(int32_t result) {
717     return result<0 ? UCOL_LESS : result==0 ? UCOL_EQUAL : UCOL_GREATER;
718 }
719 
720 class CollatorThreadTest : public SimpleThread
721 {
722 private:
723     const Collator *coll;
724     const Line *lines;
725     int32_t noLines;
726     UBool isAtLeastUCA62;
727 public:
CollatorThreadTest()728     CollatorThreadTest()  : SimpleThread(),
729         coll(NULL),
730         lines(NULL),
731         noLines(0),
732         isAtLeastUCA62(true)
733     {
734     }
setCollator(Collator * c,Line * l,int32_t nl,UBool atLeastUCA62)735     void setCollator(Collator *c, Line *l, int32_t nl, UBool atLeastUCA62)
736     {
737         coll = c;
738         lines = l;
739         noLines = nl;
740         isAtLeastUCA62 = atLeastUCA62;
741     }
run()742     virtual void run() override {
743         uint8_t sk1[1024], sk2[1024];
744         uint8_t *oldSk = NULL, *newSk = sk1;
745         int32_t oldLen = 0;
746         int32_t prev = 0;
747         int32_t i = 0;
748 
749         for(i = 0; i < noLines; i++) {
750             if(lines[i].buflen == 0) { continue; }
751 
752             int32_t resLen = coll->getSortKey(lines[i].buff, lines[i].buflen, newSk, 1024);
753 
754             if(oldSk != NULL) {
755                 int32_t skres = strcmp((char *)oldSk, (char *)newSk);
756                 int32_t cmpres = coll->compare(lines[prev].buff, lines[prev].buflen, lines[i].buff, lines[i].buflen);
757                 int32_t cmpres2 = coll->compare(lines[i].buff, lines[i].buflen, lines[prev].buff, lines[prev].buflen);
758 
759                 if(cmpres != -cmpres2) {
760                     IntlTest::gTest->errln(UnicodeString(u"Compare result not symmetrical on line ") + (i + 1));
761                     break;
762                 }
763 
764                 if(cmpres != normalizeResult(skres)) {
765                     IntlTest::gTest->errln(UnicodeString(u"Difference between coll->compare and sortkey compare on line ") + (i + 1));
766                     break;
767                 }
768 
769                 int32_t res = cmpres;
770                 if(res == 0 && !isAtLeastUCA62) {
771                     // Up to UCA 6.1, the collation test files use a custom tie-breaker,
772                     // comparing the raw input strings.
773                     res = u_strcmpCodePointOrder(lines[prev].buff, lines[i].buff);
774                     // Starting with UCA 6.2, the collation test files use the standard UCA tie-breaker,
775                     // comparing the NFD versions of the input strings,
776                     // which we do via setting strength=identical.
777                 }
778                 if(res > 0) {
779                     IntlTest::gTest->errln(UnicodeString(u"Line is not greater or equal than previous line, for line ") + (i + 1));
780                     break;
781                 }
782             }
783 
784             oldSk = newSk;
785             oldLen = resLen;
786             (void)oldLen;   // Suppress set but not used warning.
787             prev = i;
788 
789             newSk = (newSk == sk1)?sk2:sk1;
790         }
791     }
792 };
793 
TestCollators()794 void MultithreadTest::TestCollators()
795 {
796 
797     UErrorCode status = U_ZERO_ERROR;
798     FILE *testFile = NULL;
799     char testDataPath[1024];
800     strcpy(testDataPath, IntlTest::getSourceTestData(status));
801     if (U_FAILURE(status)) {
802         errln("ERROR: could not open test data %s", u_errorName(status));
803         return;
804     }
805     strcat(testDataPath, "CollationTest_");
806 
807     const char* type = "NON_IGNORABLE";
808 
809     const char *ext = ".txt";
810     if(testFile) {
811         fclose(testFile);
812     }
813     char buffer[1024];
814     strcpy(buffer, testDataPath);
815     strcat(buffer, type);
816     size_t bufLen = strlen(buffer);
817 
818     // we try to open 3 files:
819     // path/CollationTest_type.txt
820     // path/CollationTest_type_SHORT.txt
821     // path/CollationTest_type_STUB.txt
822     // we are going to test with the first one that we manage to open.
823 
824     strcpy(buffer+bufLen, ext);
825 
826     testFile = fopen(buffer, "rb");
827 
828     if(testFile == 0) {
829         strcpy(buffer+bufLen, "_SHORT");
830         strcat(buffer, ext);
831         testFile = fopen(buffer, "rb");
832 
833         if(testFile == 0) {
834             strcpy(buffer+bufLen, "_STUB");
835             strcat(buffer, ext);
836             testFile = fopen(buffer, "rb");
837 
838             if (testFile == 0) {
839                 *(buffer+bufLen) = 0;
840                 dataerrln("could not open any of the conformance test files, tried opening base %s", buffer);
841                 return;
842             } else {
843                 infoln(
844                     "INFO: Working with the stub file.\n"
845                     "If you need the full conformance test, please\n"
846                     "download the appropriate data files from:\n"
847                     "https://github.com/unicode-org/cldr/tree/main/common/uca");
848             }
849         }
850     }
851 
852     LocalArray<Line> lines(new Line[200000]);
853     memset(lines.getAlias(), 0, sizeof(Line)*200000);
854     int32_t lineNum = 0;
855 
856     UChar bufferU[1024];
857     uint32_t first = 0;
858 
859     while (fgets(buffer, 1024, testFile) != NULL) {
860         if(*buffer == 0 || buffer[0] == '#') {
861             // Store empty and comment lines so that errors are reported
862             // for the real test file lines.
863             lines[lineNum].buflen = 0;
864             lines[lineNum].buff[0] = 0;
865         } else {
866             int32_t buflen = u_parseString(buffer, bufferU, 1024, &first, &status);
867             lines[lineNum].buflen = buflen;
868             u_memcpy(lines[lineNum].buff, bufferU, buflen);
869             lines[lineNum].buff[buflen] = 0;
870         }
871         lineNum++;
872     }
873     fclose(testFile);
874     if(U_FAILURE(status)) {
875       dataerrln("Couldn't read the test file!");
876       return;
877     }
878 
879     UVersionInfo uniVersion;
880     static const UVersionInfo v62 = { 6, 2, 0, 0 };
881     u_getUnicodeVersion(uniVersion);
882     UBool isAtLeastUCA62 = uprv_memcmp(uniVersion, v62, 4) >= 0;
883 
884     LocalPointer<Collator> coll(Collator::createInstance(Locale::getRoot(), status));
885     if(U_FAILURE(status)) {
886         errcheckln(status, "Couldn't open UCA collator");
887         return;
888     }
889     coll->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
890     coll->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status);
891     coll->setAttribute(UCOL_CASE_LEVEL, UCOL_OFF, status);
892     coll->setAttribute(UCOL_STRENGTH, isAtLeastUCA62 ? UCOL_IDENTICAL : UCOL_TERTIARY, status);
893     coll->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE, status);
894 
895     int32_t spawnResult = 0;
896     LocalArray<CollatorThreadTest> tests(new CollatorThreadTest[kCollatorThreadThreads]);
897 
898     logln(UnicodeString(u"Spawning: ") + kCollatorThreadThreads + u" threads * " + kFormatThreadIterations + u" iterations each.");
899     int32_t j = 0;
900     for(j = 0; j < kCollatorThreadThreads; j++) {
901         //logln("Setting collator %i", j);
902         tests[j].setCollator(coll.getAlias(), lines.getAlias(), lineNum, isAtLeastUCA62);
903     }
904     for(j = 0; j < kCollatorThreadThreads; j++) {
905         log("%i ", j);
906         spawnResult = tests[j].start();
907         if(spawnResult != 0) {
908             errln("%s:%d THREAD INFO: thread %d failed to start with status %d", __FILE__, __LINE__, j, spawnResult);
909             return;
910         }
911     }
912     logln("Spawned all");
913 
914     for(int32_t i=0;i<kCollatorThreadThreads;i++) {
915         tests[i].join();
916         //logln(UnicodeString("Test #") + i + " is complete.. ");
917     }
918 }
919 
920 #endif /* #if !UCONFIG_NO_COLLATION */
921 
922 
923 
924 
925 //-------------------------------------------------------------------------------------------
926 //
927 //   StringThreadTest2
928 //
929 //-------------------------------------------------------------------------------------------
930 
931 const int kStringThreadIterations = 2500;// # of iterations per thread
932 const int kStringThreadThreads    = 10;  // # of threads to spawn
933 
934 
935 class StringThreadTest2 : public SimpleThread
936 {
937 public:
938     int                 fNum;
939     int                 fTraceInfo;
940     static const UnicodeString *gSharedString;
941 
StringThreadTest2()942     StringThreadTest2() // constructor is NOT multithread safe.
943         : SimpleThread(),
944         fTraceInfo(0)
945     {
946     }
947 
948 
run()949     virtual void run() override
950     {
951         fTraceInfo    = 1;
952         int loopCount = 0;
953 
954         for (loopCount = 0; loopCount < kStringThreadIterations; loopCount++) {
955             if (*gSharedString != u"This is the original test string.") {
956                 IntlTest::gTest->errln("%s:%d Original string is corrupt.", __FILE__, __LINE__);
957                 break;
958             }
959             UnicodeString s1 = *gSharedString;
960             s1 += u"cat this";
961             UnicodeString s2(s1);
962             UnicodeString s3 = *gSharedString;
963             s2 = s3;
964             s3.truncate(12);
965             s2.truncate(0);
966         }
967 
968         fTraceInfo = 2;
969     }
970 
971 };
972 
973 const UnicodeString *StringThreadTest2::gSharedString = NULL;
974 
975 // ** The actual test function.
976 
977 
TestString()978 void MultithreadTest::TestString()
979 {
980     int     j;
981     StringThreadTest2::gSharedString = new UnicodeString(u"This is the original test string.");
982     StringThreadTest2  tests[kStringThreadThreads];
983 
984     logln(UnicodeString(u"Spawning: ") + kStringThreadThreads + u" threads * " + kStringThreadIterations + u" iterations each.");
985     for(j = 0; j < kStringThreadThreads; j++) {
986         int32_t threadStatus = tests[j].start();
987         if (threadStatus != 0) {
988             errln("%s:%d System Error %d starting thread number %d.", __FILE__, __LINE__, threadStatus, j);
989         }
990     }
991 
992     // Force a failure, to verify test is functioning and can report errors.
993     // const_cast<UnicodeString *>(StringThreadTest2::gSharedString)->setCharAt(5, 'x');
994 
995     for(j=0; j<kStringThreadThreads; j++) {
996         tests[j].join();
997         logln(UnicodeString(u"Test #") + j + u" is complete.. ");
998     }
999 
1000     delete StringThreadTest2::gSharedString;
1001     StringThreadTest2::gSharedString = NULL;
1002 }
1003 
1004 
1005 //
1006 // Test for ticket #10673, race in cache code in AnyTransliterator.
1007 // It's difficult to make the original unsafe code actually fail, but
1008 // this test will fairly reliably take the code path for races in
1009 // populating the cache.
1010 //
1011 
1012 #if !UCONFIG_NO_TRANSLITERATION
1013 Transliterator *gSharedTranslit = NULL;
1014 class TxThread: public SimpleThread {
1015   public:
TxThread()1016     TxThread() {}
1017     ~TxThread();
1018     void run() override;
1019 };
1020 
~TxThread()1021 TxThread::~TxThread() {}
run()1022 void TxThread::run() {
1023     UnicodeString greekString(u"διαφορετικούς");
1024     gSharedTranslit->transliterate(greekString);
1025     IntlTest::gTest->assertEquals(WHERE, UnicodeString(u"diaphoretikoús"), greekString);
1026 }
1027 #endif
1028 
1029 
TestAnyTranslit()1030 void MultithreadTest::TestAnyTranslit() {
1031 #if !UCONFIG_NO_TRANSLITERATION
1032     UErrorCode status = U_ZERO_ERROR;
1033     LocalPointer<Transliterator> tx(Transliterator::createInstance("Any-Latin", UTRANS_FORWARD, status));
1034     if (!assertSuccess(WHERE, status, true)) { return; }
1035 
1036     gSharedTranslit = tx.getAlias();
1037     TxThread  threads[4];
1038     int32_t i;
1039     for (i=0; i<UPRV_LENGTHOF(threads); i++) {
1040         threads[i].start();
1041     }
1042 
1043     for (i=0; i<UPRV_LENGTHOF(threads); i++) {
1044         threads[i].join();
1045     }
1046     gSharedTranslit = NULL;
1047 #endif  // !UCONFIG_NO_TRANSLITERATION
1048 }
1049 
1050 
1051 
1052 //
1053 // Unified Cache Test
1054 //
1055 
1056 // Each thread fetches a pair of objects. There are 8 distinct pairs:
1057 // ("en_US", "bs"), ("en_GB", "ca"), ("fr_FR", "ca_AD") etc.
1058 // These pairs represent 8 distinct languages
1059 
1060 // Note that only one value per language gets created in the cache.
1061 // In particular each cached value can have multiple keys.
1062 static const char *gCacheLocales[] = {
1063     "en_US", "en_GB", "fr_FR", "fr",
1064     "de", "sr_ME", "sr_BA", "sr_CS"};
1065 static const char *gCacheLocales2[] = {
1066     "bs", "ca", "ca_AD", "ca_ES",
1067     "en_US", "fi", "ff_CM", "ff_GN"};
1068 
1069 static int32_t gObjectsCreated = 0;  // protected by gCTMutex
1070 static const int32_t CACHE_LOAD = 3;
1071 
1072 class UCTMultiThreadItem : public SharedObject {
1073   public:
1074     char *value;
UCTMultiThreadItem(const char * x)1075     UCTMultiThreadItem(const char *x) : value(NULL) {
1076         value = uprv_strdup(x);
1077     }
~UCTMultiThreadItem()1078     virtual ~UCTMultiThreadItem() {
1079         uprv_free(value);
1080     }
1081 };
1082 
1083 U_NAMESPACE_BEGIN
1084 
1085 static std::mutex *gCTMutex = nullptr;
1086 static std::condition_variable *gCTConditionVar = nullptr;
1087 
1088 template<> U_EXPORT
createObject(const void * context,UErrorCode & status) const1089 const UCTMultiThreadItem *LocaleCacheKey<UCTMultiThreadItem>::createObject(
1090         const void *context, UErrorCode &status) const {
1091     const UnifiedCache *cacheContext = (const UnifiedCache *) context;
1092 
1093     if (uprv_strcmp(fLoc.getLanguage(), fLoc.getName()) != 0) {
1094         const UCTMultiThreadItem *result = NULL;
1095         if (cacheContext == NULL) {
1096             UnifiedCache::getByLocale(fLoc.getLanguage(), result, status);
1097             return result;
1098         }
1099         cacheContext->get(LocaleCacheKey<UCTMultiThreadItem>(fLoc.getLanguage()), result, status);
1100         return result;
1101     }
1102 
1103     bool firstObject = false;
1104     {
1105         std::unique_lock<std::mutex> lock(*gCTMutex);
1106         firstObject = (gObjectsCreated == 0);
1107         if (firstObject) {
1108             // Force the first object creation that comes through to wait
1109             // until other have completed. Verifies that cache doesn't
1110             // deadlock when a creation is slow.
1111 
1112             // Note that gObjectsCreated needs to be incremented from 0 to 1
1113             // early, to keep subsequent threads from entering this path.
1114             gObjectsCreated = 1;
1115             while (gObjectsCreated < 3) {
1116                 gCTConditionVar->wait(lock);
1117             }
1118         }
1119     }
1120 
1121     const UCTMultiThreadItem *result =
1122         new UCTMultiThreadItem(fLoc.getLanguage());
1123     if (result == NULL) {
1124         status = U_MEMORY_ALLOCATION_ERROR;
1125     } else {
1126         result->addRef();
1127     }
1128 
1129     // Log that we created an object. The first object was already counted,
1130     //    don't do it again.
1131     {
1132         std::unique_lock<std::mutex> lock(*gCTMutex);
1133         if (!firstObject) {
1134             gObjectsCreated += 1;
1135         }
1136         gCTConditionVar->notify_all();
1137     }
1138 
1139     return result;
1140 }
1141 
1142 U_NAMESPACE_END
1143 
1144 class UnifiedCacheThread: public SimpleThread {
1145   public:
UnifiedCacheThread(const UnifiedCache * cache,const char * loc,const char * loc2)1146     UnifiedCacheThread(
1147             const UnifiedCache *cache,
1148             const char *loc,
1149             const char *loc2) : fCache(cache), fLoc(loc), fLoc2(loc2) {}
~UnifiedCacheThread()1150     ~UnifiedCacheThread() {}
1151     void run() override;
1152     void exerciseByLocale(const Locale &);
1153     const UnifiedCache *fCache;
1154     Locale fLoc;
1155     Locale fLoc2;
1156 };
1157 
exerciseByLocale(const Locale & locale)1158 void UnifiedCacheThread::exerciseByLocale(const Locale &locale) {
1159     UErrorCode status = U_ZERO_ERROR;
1160     const UCTMultiThreadItem *origItem = NULL;
1161     fCache->get(
1162             LocaleCacheKey<UCTMultiThreadItem>(locale), fCache, origItem, status);
1163     U_ASSERT(U_SUCCESS(status));
1164     IntlTest::gTest->assertEquals(WHERE, locale.getLanguage(), origItem->value);
1165 
1166     // Fetch the same item again many times. We should always get the same
1167     // pointer since this client is already holding onto it
1168     for (int32_t i = 0; i < 1000; ++i) {
1169         const UCTMultiThreadItem *item = NULL;
1170         fCache->get(
1171                 LocaleCacheKey<UCTMultiThreadItem>(locale), fCache, item, status);
1172         IntlTest::gTest->assertTrue(WHERE, item == origItem);
1173         if (item != NULL) {
1174             item->removeRef();
1175         }
1176     }
1177     origItem->removeRef();
1178 }
1179 
run()1180 void UnifiedCacheThread::run() {
1181     // Run the exercise with 2 different locales so that we can exercise
1182     // eviction more. If each thread exercises just one locale, then
1183     // eviction can't start until the threads end.
1184     exerciseByLocale(fLoc);
1185     exerciseByLocale(fLoc2);
1186 }
1187 
TestUnifiedCache()1188 void MultithreadTest::TestUnifiedCache() {
1189 
1190     // Start with our own local cache so that we have complete control
1191     // and set the eviction policy to evict starting with 2 unused
1192     // values
1193     UErrorCode status = U_ZERO_ERROR;
1194     UnifiedCache::getInstance(status);
1195     UnifiedCache cache(status);
1196     cache.setEvictionPolicy(2, 0, status);
1197     U_ASSERT(U_SUCCESS(status));
1198 
1199     gCTMutex = new std::mutex();
1200     gCTConditionVar = new std::condition_variable();
1201 
1202     gObjectsCreated = 0;
1203 
1204     UnifiedCacheThread *threads[CACHE_LOAD][UPRV_LENGTHOF(gCacheLocales)];
1205     for (int32_t i=0; i<CACHE_LOAD; ++i) {
1206         for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
1207             // Each thread works with a pair of locales.
1208             threads[i][j] = new UnifiedCacheThread(
1209                     &cache, gCacheLocales[j], gCacheLocales2[j]);
1210             threads[i][j]->start();
1211         }
1212     }
1213 
1214     for (int32_t i=0; i<CACHE_LOAD; ++i) {
1215         for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
1216             threads[i][j]->join();
1217         }
1218     }
1219     // Because of cache eviction, we can't assert exactly how many
1220     // distinct objects get created over the course of this run.
1221     // However we know that at least 8 objects get created because that
1222     // is how many distinct languages we have in our test.
1223     if (gObjectsCreated < 8) {
1224         errln("%s:%d Too few objects created.", __FILE__, __LINE__);
1225     }
1226     // We know that each thread cannot create more than 2 objects in
1227     // the cache, and there are UPRV_LENGTHOF(gCacheLocales) pairs of
1228     // objects fetched from the cache. If the threads run in series because
1229     // of eviction, at worst case each thread creates two objects.
1230     if (gObjectsCreated > 2 * CACHE_LOAD * UPRV_LENGTHOF(gCacheLocales)) {
1231         errln("%s:%d Too many objects created, got %d, expected %d", __FILE__, __LINE__, gObjectsCreated, 2 * CACHE_LOAD * UPRV_LENGTHOF(gCacheLocales));
1232 
1233     }
1234 
1235     assertEquals(WHERE, 2, cache.unusedCount());
1236 
1237     // clean up threads
1238     for (int32_t i=0; i<CACHE_LOAD; ++i) {
1239         for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
1240             delete threads[i][j];
1241         }
1242     }
1243     delete gCTMutex;
1244     delete gCTConditionVar;
1245 }
1246 
1247 #if !UCONFIG_NO_TRANSLITERATION
1248 //
1249 //  BreakTransliterator Threading Test
1250 //     This is a test for bug #11603. Test verified to fail prior to fix.
1251 //
1252 
1253 static const Transliterator *gSharedTransliterator;
1254 static const UnicodeString *gTranslitInput;
1255 static const UnicodeString *gTranslitExpected;
1256 
1257 class BreakTranslitThread: public SimpleThread {
1258   public:
BreakTranslitThread()1259     BreakTranslitThread() {}
~BreakTranslitThread()1260     ~BreakTranslitThread() {}
1261     void run() override;
1262 };
1263 
run()1264 void BreakTranslitThread::run() {
1265     for (int i=0; i<10; i++) {
1266         icu::UnicodeString s(*gTranslitInput);
1267         gSharedTransliterator->transliterate(s);
1268         if (*gTranslitExpected != s) {
1269             IntlTest::gTest->errln("%s:%d Transliteration threading failure.", __FILE__, __LINE__);
1270             break;
1271         }
1272     }
1273 }
1274 
TestBreakTranslit()1275 void MultithreadTest::TestBreakTranslit() {
1276     UErrorCode status = U_ZERO_ERROR;
1277     UnicodeString input(
1278         u"\u0E42\u0E14\u0E22\u0E1E\u0E37\u0E49\u0E19\u0E10\u0E32\u0E19\u0E41\u0E25\u0E49\u0E27,");
1279         // Thai script, โดยพื้นฐานแล้ว
1280     gTranslitInput = &input;
1281 
1282     gSharedTransliterator = Transliterator::createInstance(
1283         UnicodeString(u"Any-Latin; Lower; NFD; [:Diacritic:]Remove; NFC; Latin-ASCII;"), UTRANS_FORWARD, status);
1284     assertSuccess(WHERE, status);
1285     if (!assertTrue(WHERE, gSharedTransliterator != nullptr)) {
1286         return;
1287     }
1288 
1289     UnicodeString expected(*gTranslitInput);
1290     gSharedTransliterator->transliterate(expected);
1291     gTranslitExpected = &expected;
1292 
1293     BreakTranslitThread threads[4];
1294     for (int i=0; i<UPRV_LENGTHOF(threads); ++i) {
1295         threads[i].start();
1296     }
1297     for (int i=0; i<UPRV_LENGTHOF(threads); ++i) {
1298         threads[i].join();
1299     }
1300 
1301     delete gSharedTransliterator;
1302     gTranslitInput = NULL;
1303     gTranslitExpected = NULL;
1304 }
1305 
1306 
1307 class TestIncDecThread : public SimpleThread {
1308 public:
TestIncDecThread()1309     TestIncDecThread() {}
1310     virtual void run() override;
1311 };
1312 
1313 static u_atomic_int32_t gIncDecCounter;
1314 
run()1315 void TestIncDecThread::run() {
1316     umtx_atomic_inc(&gIncDecCounter);
1317     for (int32_t i=0; i<5000000; ++i) {
1318         umtx_atomic_inc(&gIncDecCounter);
1319         umtx_atomic_dec(&gIncDecCounter);
1320     }
1321 }
1322 
TestIncDec()1323 void MultithreadTest::TestIncDec()
1324 {
1325     static constexpr int NUM_THREADS = 4;
1326     gIncDecCounter = 0;
1327     TestIncDecThread threads[NUM_THREADS];
1328     for (auto &thread:threads) {
1329         thread.start();
1330     }
1331     for (auto &thread:threads) {
1332         thread.join();
1333     }
1334     assertEquals(WHERE, NUM_THREADS, gIncDecCounter);
1335 }
1336 
1337 #if !UCONFIG_NO_FORMATTING
1338 static Calendar  *gSharedCalendar = {};
1339 
1340 class Test20104Thread : public SimpleThread {
1341 public:
Test20104Thread()1342     Test20104Thread() {}
1343     virtual void run() override;
1344 };
1345 
run()1346 void Test20104Thread::run() {
1347     gSharedCalendar->defaultCenturyStartYear();
1348 }
1349 
Test20104()1350 void MultithreadTest::Test20104() {
1351     UErrorCode status = U_ZERO_ERROR;
1352     Locale loc("hi_IN");
1353     gSharedCalendar = new IndianCalendar(loc, status);
1354     assertSuccess(WHERE, status);
1355 
1356     static constexpr int NUM_THREADS = 4;
1357     Test20104Thread threads[NUM_THREADS];
1358     for (auto &thread:threads) {
1359         thread.start();
1360     }
1361     for (auto &thread:threads) {
1362         thread.join();
1363     }
1364     delete gSharedCalendar;
1365     // Note: failure is reported by Thread Sanitizer. Test itself succeeds.
1366 }
1367 #endif /* !UCONFIG_NO_FORMATTING */
1368 
1369 #endif /* !UCONFIG_NO_TRANSLITERATION */
1370