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