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