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