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