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