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