1 // Copyright (C) 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