blob: 7cff70051b96c2885b6c542229c6deee92938936 [file] [log] [blame]
Frank Tang3e05d9d2021-11-08 14:04:04 -08001// © 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
33MultithreadTest::MultithreadTest()
34{
35}
36
37MultithreadTest::~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
58void 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
95class TestThreadsThread : public SimpleThread
96{
97public:
98 TestThreadsThread(char* whatToChange) { fWhatToChange = whatToChange; }
99 virtual void run() override {
100 Mutex m;
101 *fWhatToChange = '*';
102 }
103private:
104 char *fWhatToChange;
105};
106
107
108void 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
159class TestArabicShapeThreads : public SimpleThread
160{
161public:
162 TestArabicShapeThreads() {}
163 virtual void run() override { doTailTest(); }
164private:
165 void doTailTest();
166};
167
168
169void 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
214void 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.
242UnicodeString 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
276const int kFormatThreadIterations = 100; // # of iterations per thread
277const int kFormatThreadThreads = 10; // # of threads to spawn
278
279#if !UCONFIG_NO_FORMATTING
280
281
282
283struct FormatThreadTestData
284{
285 double number;
286 UnicodeString string;
287 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
293static 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 */
329class 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
340const ThreadSafeFormatSharedData *gSharedData = NULL;
341
342ThreadSafeFormatSharedData::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
356ThreadSafeFormatSharedData::~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 */
367class ThreadSafeFormat {
368public:
369 /* give a unique offset to each thread */
370 ThreadSafeFormat(UErrorCode &status);
371 UBool doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const;
372private:
373 LocalPointer<NumberFormat> fFormat; // formatter - en_US constructed currency
374};
375
376
377ThreadSafeFormat::ThreadSafeFormat(UErrorCode &status) {
378 fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status));
379}
380
381static const UChar *kUSD = u"USD";
382
383UBool ThreadSafeFormat::doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const {
Frank Tang1f164ee2022-11-08 12:31:27 -0800384 UBool okay = true;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800385
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"! ");
Frank Tang1f164ee2022-11-08 12:31:27 -0800392 okay = false;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800393 }
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"! ");
Frank Tang1f164ee2022-11-08 12:31:27 -0800401 okay = false;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800402 }
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));
Frank Tang1f164ee2022-11-08 12:31:27 -0800417 okay = false;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800418 }
419 return okay;
420}
421
422UBool U_CALLCONV isAcceptable(void *, const char *, const char *, const UDataInfo *) {
Frank Tang1f164ee2022-11-08 12:31:27 -0800423 return true;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800424}
425
426//static UMTX debugMutex = NULL;
427//static UMTX gDebugMutex;
428
429
430class FormatThreadTest : public SimpleThread
431{
432public:
433 int fNum;
434 int fTraceInfo;
435
436 LocalPointer<ThreadSafeFormat> fTSF;
437
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
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
651cleanupAndReturn:
652 fTraceInfo = 2;
653 }
654
655private:
656 int32_t fOffset; // where we are testing from.
657};
658
659// ** The actual test function.
660
661void MultithreadTest::TestThreadedIntl()
662{
663 UnicodeString theErr;
664
665 UErrorCode threadSafeErr = U_ZERO_ERROR;
666
667 ThreadSafeFormatSharedData sharedData(threadSafeErr);
Frank Tang1f164ee2022-11-08 12:31:27 -0800668 assertSuccess(WHERE, threadSafeErr, true);
Frank Tang3e05d9d2021-11-08 14:04:04 -0800669
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
709struct Line {
710 UChar buff[25];
711 int32_t buflen;
712} ;
713
714
715static UCollationResult
716normalizeResult(int32_t result) {
717 return result<0 ? UCOL_LESS : result==0 ? UCOL_EQUAL : UCOL_GREATER;
718}
719
720class CollatorThreadTest : public SimpleThread
721{
722private:
723 const Collator *coll;
724 const Line *lines;
725 int32_t noLines;
726 UBool isAtLeastUCA62;
727public:
728 CollatorThreadTest() : SimpleThread(),
729 coll(NULL),
730 lines(NULL),
731 noLines(0),
Frank Tang1f164ee2022-11-08 12:31:27 -0800732 isAtLeastUCA62(true)
Frank Tang3e05d9d2021-11-08 14:04:04 -0800733 {
734 }
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 }
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
794void 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
931const int kStringThreadIterations = 2500;// # of iterations per thread
932const int kStringThreadThreads = 10; // # of threads to spawn
933
934
935class StringThreadTest2 : public SimpleThread
936{
937public:
938 int fNum;
939 int fTraceInfo;
940 static const UnicodeString *gSharedString;
941
942 StringThreadTest2() // constructor is NOT multithread safe.
943 : SimpleThread(),
944 fTraceInfo(0)
945 {
946 }
947
948
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
973const UnicodeString *StringThreadTest2::gSharedString = NULL;
974
975// ** The actual test function.
976
977
978void 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
1013Transliterator *gSharedTranslit = NULL;
1014class TxThread: public SimpleThread {
1015 public:
1016 TxThread() {}
1017 ~TxThread();
1018 void run() override;
1019};
1020
1021TxThread::~TxThread() {}
1022void 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
1030void 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.
1062static const char *gCacheLocales[] = {
1063 "en_US", "en_GB", "fr_FR", "fr",
1064 "de", "sr_ME", "sr_BA", "sr_CS"};
1065static const char *gCacheLocales2[] = {
1066 "bs", "ca", "ca_AD", "ca_ES",
1067 "en_US", "fi", "ff_CM", "ff_GN"};
1068
1069static int32_t gObjectsCreated = 0; // protected by gCTMutex
1070static const int32_t CACHE_LOAD = 3;
1071
1072class UCTMultiThreadItem : public SharedObject {
1073 public:
1074 char *value;
1075 UCTMultiThreadItem(const char *x) : value(NULL) {
1076 value = uprv_strdup(x);
1077 }
1078 virtual ~UCTMultiThreadItem() {
1079 uprv_free(value);
1080 }
1081};
1082
1083U_NAMESPACE_BEGIN
1084
1085static std::mutex *gCTMutex = nullptr;
1086static std::condition_variable *gCTConditionVar = nullptr;
1087
1088template<> U_EXPORT
1089const 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
1142U_NAMESPACE_END
1143
1144class UnifiedCacheThread: public SimpleThread {
1145 public:
1146 UnifiedCacheThread(
1147 const UnifiedCache *cache,
1148 const char *loc,
1149 const char *loc2) : fCache(cache), fLoc(loc), fLoc2(loc2) {}
1150 ~UnifiedCacheThread() {}
1151 void run() override;
1152 void exerciseByLocale(const Locale &);
1153 const UnifiedCache *fCache;
1154 Locale fLoc;
1155 Locale fLoc2;
1156};
1157
1158void 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
1180void 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
1188void 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
1253static const Transliterator *gSharedTransliterator;
1254static const UnicodeString *gTranslitInput;
1255static const UnicodeString *gTranslitExpected;
1256
1257class BreakTranslitThread: public SimpleThread {
1258 public:
1259 BreakTranslitThread() {}
1260 ~BreakTranslitThread() {}
1261 void run() override;
1262};
1263
1264void 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
1275void 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
1307class TestIncDecThread : public SimpleThread {
1308public:
1309 TestIncDecThread() {}
1310 virtual void run() override;
1311};
1312
1313static u_atomic_int32_t gIncDecCounter;
1314
1315void 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
1323void 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
1338static Calendar *gSharedCalendar = {};
1339
1340class Test20104Thread : public SimpleThread {
1341public:
1342 Test20104Thread() {}
1343 virtual void run() override;
1344};
1345
1346void Test20104Thread::run() {
1347 gSharedCalendar->defaultCenturyStartYear();
1348}
1349
1350void 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 */