1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /********************************************************************
5 * Copyright (c) 1999-2015, International Business Machines Corporation and
6 * others. All Rights Reserved.
7 ********************************************************************/
9 #include "simplethread.h"
11 #include "unicode/utypes.h"
12 #include "unicode/ustring.h"
16 #include "indiancal.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"
26 #include "unicode/ushape.h"
27 #include "unicode/translit.h"
28 #include "sharedobject.h"
29 #include "unifiedcache.h"
33 MultithreadTest::MultithreadTest()
37 MultithreadTest::~MultithreadTest()
43 #include <ctype.h> // tolower, toupper
46 #include "unicode/putil.h"
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"
58 void MultithreadTest::runIndexedTest( int32_t index
, UBool exec
,
59 const char* &name
, char* /*par*/ ) {
61 logln("TestSuite MultithreadTest: ");
64 TESTCASE_AUTO(TestThreads
);
65 TESTCASE_AUTO(TestMutex
);
66 #if !UCONFIG_NO_FORMATTING
67 TESTCASE_AUTO(TestThreadedIntl
);
69 #if !UCONFIG_NO_COLLATION
70 TESTCASE_AUTO(TestCollators
);
71 #endif /* #if !UCONFIG_NO_COLLATION */
72 TESTCASE_AUTO(TestString
);
73 TESTCASE_AUTO(TestArabicShapingThreads
);
74 TESTCASE_AUTO(TestAnyTranslit
);
75 TESTCASE_AUTO(TestConditionVariables
);
76 TESTCASE_AUTO(TestUnifiedCache
);
77 #if !UCONFIG_NO_TRANSLITERATION
78 TESTCASE_AUTO(TestBreakTranslit
);
79 TESTCASE_AUTO(TestIncDec
);
80 #if !UCONFIG_NO_FORMATTING
81 TESTCASE_AUTO(Test20104
);
82 #endif /* #if !UCONFIG_NO_FORMATTING */
83 #endif /* #if !UCONFIG_NO_TRANSLITERATION */
88 //-----------------------------------------------------------------------------------
90 // TestThreads -- see if threads really work at all.
92 // Set up N threads pointing at N chars. When they are started, they will
93 // set their chars. At the end we make sure they are all set.
95 //-----------------------------------------------------------------------------------
97 class TestThreadsThread
: public SimpleThread
100 TestThreadsThread(char* whatToChange
) { fWhatToChange
= whatToChange
; }
101 virtual void run() { Mutex m
;
102 *fWhatToChange
= '*';
109 void MultithreadTest::TestThreads()
111 static const int32_t THREADTEST_NRTHREADS
= 8;
112 char threadTestChars
[THREADTEST_NRTHREADS
+ 1];
113 SimpleThread
*threads
[THREADTEST_NRTHREADS
];
114 int32_t numThreadsStarted
= 0;
117 for(i
=0;i
<THREADTEST_NRTHREADS
;i
++)
119 threadTestChars
[i
] = ' ';
120 threads
[i
] = new TestThreadsThread(&threadTestChars
[i
]);
122 threadTestChars
[THREADTEST_NRTHREADS
] = '\0';
124 logln("->" + UnicodeString(threadTestChars
) + "<- Firing off threads.. ");
125 for(i
=0;i
<THREADTEST_NRTHREADS
;i
++)
127 if (threads
[i
]->start() != 0) {
128 errln("Error starting thread %d", i
);
133 logln(" Subthread started.");
136 assertTrue(WHERE
, THREADTEST_NRTHREADS
== numThreadsStarted
);
138 logln("Waiting for threads to be set..");
139 for(i
=0; i
<THREADTEST_NRTHREADS
; i
++) {
141 if (threadTestChars
[i
] != '*') {
142 errln("%s:%d Thread %d failed.", __FILE__
, __LINE__
, i
);
149 //-----------------------------------------------------------------------------------
151 // TestArabicShapeThreads -- see if calls to u_shapeArabic in many threads works successfully
153 // Set up N threads pointing at N chars. When they are started, they will make calls to doTailTest which tests
154 // u_shapeArabic, if the calls are successful it will the set * chars.
155 // At the end we make sure all threads managed to run u_shapeArabic successfully.
156 // This is a unit test for ticket 9473
158 //-----------------------------------------------------------------------------------
160 class TestArabicShapeThreads
: public SimpleThread
163 TestArabicShapeThreads() {};
164 virtual void run() { doTailTest(); };
170 void TestArabicShapeThreads::doTailTest(void) {
171 static const UChar src
[] = { 0x0020, 0x0633, 0 };
172 static const UChar dst_old
[] = { 0xFEB1, 0x200B,0 };
173 static const UChar dst_new
[] = { 0xFEB1, 0xFE73,0 };
174 UChar dst
[3] = { 0x0000, 0x0000,0 };
178 for (int32_t loopCount
= 0; loopCount
< 100; loopCount
++) {
179 status
= U_ZERO_ERROR
;
180 length
= u_shapeArabic(src
, -1, dst
, UPRV_LENGTHOF(dst
),
181 U_SHAPE_LETTERS_SHAPE
|U_SHAPE_SEEN_TWOCELL_NEAR
, &status
);
182 if(U_FAILURE(status
)) {
183 IntlTest::gTest
->errln("Fail: status %s\n", u_errorName(status
));
185 } else if(length
!=2) {
186 IntlTest::gTest
->errln("Fail: len %d expected 3\n", length
);
188 } else if(u_strncmp(dst
,dst_old
,UPRV_LENGTHOF(dst
))) {
189 IntlTest::gTest
->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
190 dst
[0],dst
[1],dst_old
[0],dst_old
[1]);
196 status
= U_ZERO_ERROR
;
197 length
= u_shapeArabic(src
, -1, dst
, UPRV_LENGTHOF(dst
),
198 U_SHAPE_LETTERS_SHAPE
|U_SHAPE_SEEN_TWOCELL_NEAR
|U_SHAPE_TAIL_NEW_UNICODE
, &status
);
199 if(U_FAILURE(status
)) {
200 IntlTest::gTest
->errln("Fail: status %s\n", u_errorName(status
));
202 } else if(length
!=2) {
203 IntlTest::gTest
->errln("Fail: len %d expected 3\n", length
);
205 } else if(u_strncmp(dst
,dst_new
,UPRV_LENGTHOF(dst
))) {
206 IntlTest::gTest
->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
207 dst
[0],dst
[1],dst_new
[0],dst_new
[1]);
215 void MultithreadTest::TestArabicShapingThreads()
217 TestArabicShapeThreads threads
[30];
221 logln("-> do TestArabicShapingThreads <- Firing off threads.. ");
222 for(i
=0; i
< UPRV_LENGTHOF(threads
); i
++) {
223 if (threads
[i
].start() != 0) {
224 errln("Error starting thread %d", i
);
228 for(i
=0; i
< UPRV_LENGTHOF(threads
); i
++) {
231 logln("->TestArabicShapingThreads <- Got all threads! cya");
235 //-----------------------------------------------------------------------
237 // TestMutex - a simple (non-stress) test to verify that ICU mutexes
238 // and condition variables are functioning. Does not test the use of
239 // mutexes within ICU services, but rather that the
240 // platform's mutex support is at least superficially there.
242 //----------------------------------------------------------------------
243 static UMutex
*gTestMutexA() {
244 static UMutex
*m
= STATIC_NEW(UMutex
);
247 static std::condition_variable
*gThreadsCountChanged() {
248 static std::condition_variable
*cv
= STATIC_NEW(std::condition_variable
);
252 static int gThreadsStarted
= 0;
253 static int gThreadsInMiddle
= 0;
254 static int gThreadsDone
= 0;
256 static const int TESTMUTEX_THREAD_COUNT
= 40;
258 class TestMutexThread
: public SimpleThread
262 // This is the code that each of the spawned threads runs.
263 // All threads move together throught the started - middle - done sequence together,
264 // waiting for all other threads to reach each point before advancing.
265 std::unique_lock
<std::mutex
> lock(gTestMutexA()->fMutex
);
266 gThreadsStarted
+= 1;
267 gThreadsCountChanged()->notify_all();
268 while (gThreadsStarted
< TESTMUTEX_THREAD_COUNT
) {
269 if (gThreadsInMiddle
!= 0) {
270 IntlTest::gTest
->errln(
271 "%s:%d gThreadsInMiddle = %d. Expected 0.", __FILE__
, __LINE__
, gThreadsInMiddle
);
274 gThreadsCountChanged()->wait(lock
);
277 gThreadsInMiddle
+= 1;
278 gThreadsCountChanged()->notify_all();
279 while (gThreadsInMiddle
< TESTMUTEX_THREAD_COUNT
) {
280 if (gThreadsDone
!= 0) {
281 IntlTest::gTest
->errln(
282 "%s:%d gThreadsDone = %d. Expected 0.", __FILE__
, __LINE__
, gThreadsDone
);
285 gThreadsCountChanged()->wait(lock
);
289 gThreadsCountChanged()->notify_all();
290 while (gThreadsDone
< TESTMUTEX_THREAD_COUNT
) {
291 gThreadsCountChanged()->wait(lock
);
296 void MultithreadTest::TestMutex()
299 gThreadsInMiddle
= 0;
302 TestMutexThread threads
[TESTMUTEX_THREAD_COUNT
];
304 std::unique_lock
<std::mutex
> lock(gTestMutexA()->fMutex
);
305 for (i
=0; i
<TESTMUTEX_THREAD_COUNT
; i
++) {
306 if (threads
[i
].start() != 0) {
307 errln("%s:%d Error starting thread %d", __FILE__
, __LINE__
, i
);
312 // Because we are holding gTestMutexA, all of the threads should be blocked
313 // at the start of their run() function.
314 if (gThreadsStarted
!= 0) {
315 errln("%s:%d gThreadsStarted=%d. Expected 0.", __FILE__
, __LINE__
, gThreadsStarted
);
319 while (gThreadsInMiddle
< TESTMUTEX_THREAD_COUNT
) {
320 if (gThreadsDone
!= 0) {
321 errln("%s:%d gThreadsDone=%d. Expected 0.", __FILE__
, __LINE__
, gThreadsStarted
);
324 gThreadsCountChanged()->wait(lock
);
327 while (gThreadsDone
< TESTMUTEX_THREAD_COUNT
) {
328 gThreadsCountChanged()->wait(lock
);
331 for (i
=0; i
<TESTMUTEX_THREAD_COUNT
; i
++) {
337 //-------------------------------------------------------------------------------------------
339 // TestMultithreadedIntl. Test ICU Formatting in a multi-threaded environment
341 //-------------------------------------------------------------------------------------------
344 // * Show exactly where the string's differences lie.
345 UnicodeString
showDifference(const UnicodeString
& expected
, const UnicodeString
& result
)
348 res
= expected
+ u
"<Expected\n";
349 if(expected
.length() != result
.length())
350 res
+= u
" [ Different lengths ] \n";
353 for(int32_t i
=0;i
<expected
.length();i
++)
355 if(expected
[i
] == result
[i
])
364 res
+= u
"<Differences";
367 res
+= result
+ u
"<Result\n";
373 //-------------------------------------------------------------------------------------------
375 // FormatThreadTest - a thread that tests performing a number of numberformats.
377 //-------------------------------------------------------------------------------------------
379 const int kFormatThreadIterations
= 100; // # of iterations per thread
380 const int kFormatThreadThreads
= 10; // # of threads to spawn
382 #if !UCONFIG_NO_FORMATTING
386 struct FormatThreadTestData
389 UnicodeString string
;
390 FormatThreadTestData(double a
, const UnicodeString
& b
) : number(a
),string(b
) {}
394 // "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3 number,currency}."
396 static void formatErrorMessage(UErrorCode
&realStatus
, const UnicodeString
& pattern
, const Locale
& theLocale
,
397 UErrorCode inStatus0
, // statusString 1
398 const Locale
&inCountry2
, double currency3
, // these numbers are the message arguments.
399 UnicodeString
&result
)
401 if(U_FAILURE(realStatus
))
402 return; // you messed up
404 UnicodeString
errString1(u_errorName(inStatus0
));
406 UnicodeString countryName2
;
407 inCountry2
.getDisplayCountry(theLocale
,countryName2
);
409 Formattable myArgs
[] = {
410 Formattable((int32_t)inStatus0
), // inStatus0 {0}
411 Formattable(errString1
), // statusString1 {1}
412 Formattable(countryName2
), // inCountry2 {2}
413 Formattable(currency3
)// currency3 {3,number,currency}
416 LocalPointer
<MessageFormat
> fmt(new MessageFormat(u
"MessageFormat's API is broken!!!!!!!!!!!",realStatus
), realStatus
);
417 if (U_FAILURE(realStatus
)) {
420 fmt
->setLocale(theLocale
);
421 fmt
->applyPattern(pattern
, realStatus
);
423 FieldPosition ignore
= 0;
424 fmt
->format(myArgs
,4,result
,ignore
,realStatus
);
428 * Shared formatters & data used by instances of ThreadSafeFormat.
429 * Exactly one instance of this class is created, and it is then shared concurrently
430 * by the multiple instances of ThreadSafeFormat.
432 class ThreadSafeFormatSharedData
{
434 ThreadSafeFormatSharedData(UErrorCode
&status
);
435 ~ThreadSafeFormatSharedData();
436 LocalPointer
<NumberFormat
> fFormat
;
437 Formattable fYDDThing
;
438 Formattable fBBDThing
;
439 UnicodeString fYDDStr
;
440 UnicodeString fBBDStr
;
443 const ThreadSafeFormatSharedData
*gSharedData
= NULL
;
445 ThreadSafeFormatSharedData::ThreadSafeFormatSharedData(UErrorCode
&status
) {
446 fFormat
.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status
));
447 static const UChar
*kYDD
= u
"YDD";
448 static const UChar
*kBBD
= u
"BBD";
449 fYDDThing
.adoptObject(new CurrencyAmount(123.456, kYDD
, status
));
450 fBBDThing
.adoptObject(new CurrencyAmount(987.654, kBBD
, status
));
451 if (U_FAILURE(status
)) {
454 fFormat
->format(fYDDThing
, fYDDStr
, NULL
, status
);
455 fFormat
->format(fBBDThing
, fBBDStr
, NULL
, status
);
459 ThreadSafeFormatSharedData::~ThreadSafeFormatSharedData() {
464 * Class for thread-safe testing of format.
465 * Instances of this class appear as members of class FormatThreadTest.
466 * Multiple instances of FormatThreadTest coexist.
467 * ThreadSafeFormat::doStuff() is called concurrently to test the thread safety of
468 * various shared format operations.
470 class ThreadSafeFormat
{
472 /* give a unique offset to each thread */
473 ThreadSafeFormat(UErrorCode
&status
);
474 UBool
doStuff(int32_t offset
, UnicodeString
&appendErr
, UErrorCode
&status
) const;
476 LocalPointer
<NumberFormat
> fFormat
; // formatter - en_US constructed currency
480 ThreadSafeFormat::ThreadSafeFormat(UErrorCode
&status
) {
481 fFormat
.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status
));
484 static const UChar
*kUSD
= u
"USD";
486 UBool
ThreadSafeFormat::doStuff(int32_t offset
, UnicodeString
&appendErr
, UErrorCode
&status
) const {
489 if(u_strcmp(fFormat
->getCurrency(), kUSD
)) {
490 appendErr
.append(u
"fFormat currency != ")
493 .append(fFormat
->getCurrency())
498 if(u_strcmp(gSharedData
->fFormat
->getCurrency(), kUSD
)) {
499 appendErr
.append(u
"gFormat currency != ")
502 .append(gSharedData
->fFormat
->getCurrency())
507 const UnicodeString
*o
=NULL
;
509 const NumberFormat
*nf
= NULL
; // only operate on it as const.
511 case 0: f
= gSharedData
->fYDDThing
; o
= &gSharedData
->fYDDStr
; nf
= gSharedData
->fFormat
.getAlias(); break;
512 case 1: f
= gSharedData
->fBBDThing
; o
= &gSharedData
->fBBDStr
; nf
= gSharedData
->fFormat
.getAlias(); break;
513 case 2: f
= gSharedData
->fYDDThing
; o
= &gSharedData
->fYDDStr
; nf
= fFormat
.getAlias(); break;
514 case 3: f
= gSharedData
->fBBDThing
; o
= &gSharedData
->fBBDStr
; nf
= fFormat
.getAlias(); break;
516 nf
->format(f
, str
, NULL
, status
);
519 appendErr
.append(showDifference(*o
, str
));
525 UBool U_CALLCONV
isAcceptable(void *, const char *, const char *, const UDataInfo
*) {
529 //static UMTX debugMutex = NULL;
530 //static UMTX gDebugMutex;
533 class FormatThreadTest
: public SimpleThread
539 LocalPointer
<ThreadSafeFormat
> fTSF
;
541 FormatThreadTest() // constructor is NOT multithread safe.
549 UErrorCode status
= U_ZERO_ERROR
; // TODO: rearrange code to allow checking of status.
550 fTSF
.adoptInstead(new ThreadSafeFormat(status
));
551 static int32_t fgOffset
= 0;
560 LocalPointer
<NumberFormat
> percentFormatter
;
561 UErrorCode status
= U_ZERO_ERROR
;
565 for (int i
=0; i
<4000; i
++) {
566 status
= U_ZERO_ERROR
;
567 UDataMemory
*data1
= udata_openChoice(0, "res", "en_US", isAcceptable
, 0, &status
);
568 UDataMemory
*data2
= udata_openChoice(0, "res", "fr", isAcceptable
, 0, &status
);
571 if (U_FAILURE(status
)) {
572 error("udata_openChoice failed.\n");
582 for (m
=0; m
<4000; m
++) {
583 status
= U_ZERO_ERROR
;
584 UResourceBundle
*res
= NULL
;
585 const char *localeName
= NULL
;
587 Locale loc
= Locale::getEnglish();
589 localeName
= loc
.getName();
590 // localeName = "en";
592 // ResourceBundle bund = ResourceBundle(0, loc, status);
593 //umtx_lock(&gDebugMutex);
594 res
= ures_open(NULL
, localeName
, &status
);
595 //umtx_unlock(&gDebugMutex);
597 //umtx_lock(&gDebugMutex);
599 //umtx_unlock(&gDebugMutex);
601 if (U_FAILURE(status
)) {
602 error("Resource bundle construction failed.\n");
609 // Keep this data here to avoid static initialization.
610 FormatThreadTestData kNumberFormatTestData
[] =
612 FormatThreadTestData((double)5.0, UnicodeString(u
"5")),
613 FormatThreadTestData( 6.0, UnicodeString(u
"6")),
614 FormatThreadTestData( 20.0, UnicodeString(u
"20")),
615 FormatThreadTestData( 8.0, UnicodeString(u
"8")),
616 FormatThreadTestData( 8.3, UnicodeString(u
"8.3")),
617 FormatThreadTestData( 12345, UnicodeString(u
"12,345")),
618 FormatThreadTestData( 81890.23, UnicodeString(u
"81,890.23")),
620 int32_t kNumberFormatTestDataLength
= UPRV_LENGTHOF(kNumberFormatTestData
);
622 // Keep this data here to avoid static initialization.
623 FormatThreadTestData kPercentFormatTestData
[] =
625 FormatThreadTestData((double)5.0, CharsToUnicodeString("500\\u00a0%")),
626 FormatThreadTestData( 1.0, CharsToUnicodeString("100\\u00a0%")),
627 FormatThreadTestData( 0.26, CharsToUnicodeString("26\\u00a0%")),
628 FormatThreadTestData(
629 16384.99, CharsToUnicodeString("1\\u202F638\\u202F499\\u00a0%")), // U+202F = NNBSP
630 FormatThreadTestData(
631 81890.23, CharsToUnicodeString("8\\u202F189\\u202F023\\u00a0%")),
633 int32_t kPercentFormatTestDataLength
= UPRV_LENGTHOF(kPercentFormatTestData
);
636 status
= U_ZERO_ERROR
;
637 LocalPointer
<NumberFormat
> formatter(NumberFormat::createInstance(Locale::getEnglish(),status
));
638 if(U_FAILURE(status
)) {
639 IntlTest::gTest
->dataerrln("%s:%d Error %s on NumberFormat::createInstance().",
640 __FILE__
, __LINE__
, u_errorName(status
));
641 goto cleanupAndReturn
;
644 percentFormatter
.adoptInstead(NumberFormat::createPercentInstance(Locale::getFrench(),status
));
645 if(U_FAILURE(status
)) {
646 IntlTest::gTest
->errln("%s:%d Error %s on NumberFormat::createPercentInstance().",
647 __FILE__
, __LINE__
, u_errorName(status
));
648 goto cleanupAndReturn
;
651 for(iteration
= 0;!IntlTest::gTest
->getErrors() && iteration
<kFormatThreadIterations
;iteration
++)
654 int32_t whichLine
= (iteration
+ fOffset
)%kNumberFormatTestDataLength
;
656 UnicodeString output
;
658 formatter
->format(kNumberFormatTestData
[whichLine
].number
, output
);
660 if(0 != output
.compare(kNumberFormatTestData
[whichLine
].string
)) {
661 IntlTest::gTest
->errln("format().. expected " + kNumberFormatTestData
[whichLine
].string
663 goto cleanupAndReturn
;
666 // Now check percent.
668 whichLine
= (iteration
+ fOffset
)%kPercentFormatTestDataLength
;
670 percentFormatter
->format(kPercentFormatTestData
[whichLine
].number
, output
);
671 if(0 != output
.compare(kPercentFormatTestData
[whichLine
].string
))
673 IntlTest::gTest
->errln("percent format().. \n" +
674 showDifference(kPercentFormatTestData
[whichLine
].string
,output
));
675 goto cleanupAndReturn
;
678 // Test message error
679 const int kNumberOfMessageTests
= 3;
680 UErrorCode statusToCheck
;
681 UnicodeString patternToCheck
;
682 Locale messageLocale
;
683 Locale countryToCheck
;
684 double currencyToCheck
;
686 UnicodeString expected
;
689 switch((iteration
+fOffset
) % kNumberOfMessageTests
)
693 statusToCheck
= U_FILE_ACCESS_ERROR
;
694 patternToCheck
= u
"0:Someone from {2} is receiving a #{0}"
695 " error - {1}. Their telephone call is costing "
696 "{3,number,currency}."; // number,currency
697 messageLocale
= Locale("en","US");
698 countryToCheck
= Locale("","HR");
699 currencyToCheck
= 8192.77;
700 expected
= u
"0:Someone from Croatia is receiving a #4 error - "
701 "U_FILE_ACCESS_ERROR. Their telephone call is costing $8,192.77.";
704 statusToCheck
= U_INDEX_OUTOFBOUNDS_ERROR
;
705 patternToCheck
= u
"1:A customer in {2} is receiving a #{0} error - {1}. "
706 "Their telephone call is costing {3,number,currency}."; // number,currency
707 messageLocale
= Locale("de","DE@currency=DEM");
708 countryToCheck
= Locale("","BF");
709 currencyToCheck
= 2.32;
710 expected
= u
"1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. "
711 u
"Their telephone call is costing 2,32\u00A0DM.";
714 statusToCheck
= U_MEMORY_ALLOCATION_ERROR
;
715 patternToCheck
= u
"2:user in {2} is receiving a #{0} error - {1}. "
716 "They insist they just spent {3,number,currency} "
717 "on memory."; // number,currency
718 messageLocale
= Locale("de","AT@currency=ATS"); // Austrian German
719 countryToCheck
= Locale("","US"); // hmm
720 currencyToCheck
= 40193.12;
721 expected
= u
"2:user in Vereinigte Staaten is receiving a #7 error"
722 u
" - U_MEMORY_ALLOCATION_ERROR. They insist they just spent"
723 u
" \u00f6S\u00A040.193,12 on memory.";
727 UnicodeString result
;
728 UErrorCode status
= U_ZERO_ERROR
;
729 formatErrorMessage(status
,patternToCheck
,messageLocale
,statusToCheck
,
730 countryToCheck
,currencyToCheck
,result
);
731 if(U_FAILURE(status
))
733 UnicodeString
tmp(u_errorName(status
));
734 IntlTest::gTest
->errln(u
"Failure on message format, pattern=" + patternToCheck
+
736 goto cleanupAndReturn
;
739 if(result
!= expected
)
741 IntlTest::gTest
->errln(u
"PatternFormat: \n" + showDifference(expected
,result
));
742 goto cleanupAndReturn
;
744 // test the Thread Safe Format
745 UnicodeString appendErr
;
746 if(!fTSF
->doStuff(fNum
, appendErr
, status
)) {
747 IntlTest::gTest
->errln(appendErr
);
748 goto cleanupAndReturn
;
750 } /* end of for loop */
759 int32_t fOffset
; // where we are testing from.
762 // ** The actual test function.
764 void MultithreadTest::TestThreadedIntl()
766 UnicodeString theErr
;
768 UErrorCode threadSafeErr
= U_ZERO_ERROR
;
770 ThreadSafeFormatSharedData
sharedData(threadSafeErr
);
771 assertSuccess(WHERE
, threadSafeErr
, TRUE
);
774 // Create and start the test threads
776 logln("Spawning: %d threads * %d iterations each.",
777 kFormatThreadThreads
, kFormatThreadIterations
);
778 FormatThreadTest tests
[kFormatThreadThreads
];
780 for(j
= 0; j
< UPRV_LENGTHOF(tests
); j
++) {
782 int32_t threadStatus
= tests
[j
].start();
783 if (threadStatus
!= 0) {
784 errln("%s:%d System Error %d starting thread number %d.",
785 __FILE__
, __LINE__
, threadStatus
, j
);
791 for (j
=0; j
<UPRV_LENGTHOF(tests
); j
++) {
793 logln("Thread # %d is complete..", j
);
796 #endif /* #if !UCONFIG_NO_FORMATTING */
802 //-------------------------------------------------------------------------------------------
804 // Collation threading test
806 //-------------------------------------------------------------------------------------------
807 #if !UCONFIG_NO_COLLATION
809 #define kCollatorThreadThreads 10 // # of threads to spawn
810 #define kCollatorThreadPatience kCollatorThreadThreads*30
818 static UCollationResult
819 normalizeResult(int32_t result
) {
820 return result
<0 ? UCOL_LESS
: result
==0 ? UCOL_EQUAL
: UCOL_GREATER
;
823 class CollatorThreadTest
: public SimpleThread
826 const Collator
*coll
;
829 UBool isAtLeastUCA62
;
831 CollatorThreadTest() : SimpleThread(),
838 void setCollator(Collator
*c
, Line
*l
, int32_t nl
, UBool atLeastUCA62
)
843 isAtLeastUCA62
= atLeastUCA62
;
846 uint8_t sk1
[1024], sk2
[1024];
847 uint8_t *oldSk
= NULL
, *newSk
= sk1
;
852 for(i
= 0; i
< noLines
; i
++) {
853 if(lines
[i
].buflen
== 0) { continue; }
855 int32_t resLen
= coll
->getSortKey(lines
[i
].buff
, lines
[i
].buflen
, newSk
, 1024);
858 int32_t skres
= strcmp((char *)oldSk
, (char *)newSk
);
859 int32_t cmpres
= coll
->compare(lines
[prev
].buff
, lines
[prev
].buflen
, lines
[i
].buff
, lines
[i
].buflen
);
860 int32_t cmpres2
= coll
->compare(lines
[i
].buff
, lines
[i
].buflen
, lines
[prev
].buff
, lines
[prev
].buflen
);
862 if(cmpres
!= -cmpres2
) {
863 IntlTest::gTest
->errln(UnicodeString(u
"Compare result not symmetrical on line ") + (i
+ 1));
867 if(cmpres
!= normalizeResult(skres
)) {
868 IntlTest::gTest
->errln(UnicodeString(u
"Difference between coll->compare and sortkey compare on line ") + (i
+ 1));
872 int32_t res
= cmpres
;
873 if(res
== 0 && !isAtLeastUCA62
) {
874 // Up to UCA 6.1, the collation test files use a custom tie-breaker,
875 // comparing the raw input strings.
876 res
= u_strcmpCodePointOrder(lines
[prev
].buff
, lines
[i
].buff
);
877 // Starting with UCA 6.2, the collation test files use the standard UCA tie-breaker,
878 // comparing the NFD versions of the input strings,
879 // which we do via setting strength=identical.
882 IntlTest::gTest
->errln(UnicodeString(u
"Line is not greater or equal than previous line, for line ") + (i
+ 1));
889 (void)oldLen
; // Suppress set but not used warning.
892 newSk
= (newSk
== sk1
)?sk2
:sk1
;
897 void MultithreadTest::TestCollators()
900 UErrorCode status
= U_ZERO_ERROR
;
901 FILE *testFile
= NULL
;
902 char testDataPath
[1024];
903 strcpy(testDataPath
, IntlTest::getSourceTestData(status
));
904 if (U_FAILURE(status
)) {
905 errln("ERROR: could not open test data %s", u_errorName(status
));
908 strcat(testDataPath
, "CollationTest_");
910 const char* type
= "NON_IGNORABLE";
912 const char *ext
= ".txt";
917 strcpy(buffer
, testDataPath
);
918 strcat(buffer
, type
);
919 size_t bufLen
= strlen(buffer
);
921 // we try to open 3 files:
922 // path/CollationTest_type.txt
923 // path/CollationTest_type_SHORT.txt
924 // path/CollationTest_type_STUB.txt
925 // we are going to test with the first one that we manage to open.
927 strcpy(buffer
+bufLen
, ext
);
929 testFile
= fopen(buffer
, "rb");
932 strcpy(buffer
+bufLen
, "_SHORT");
934 testFile
= fopen(buffer
, "rb");
937 strcpy(buffer
+bufLen
, "_STUB");
939 testFile
= fopen(buffer
, "rb");
942 *(buffer
+bufLen
) = 0;
943 dataerrln("could not open any of the conformance test files, tried opening base %s", buffer
);
947 "INFO: Working with the stub file.\n"
948 "If you need the full conformance test, please\n"
949 "download the appropriate data files from:\n"
950 "http://source.icu-project.org/repos/icu/tools/trunk/unicodetools/com/ibm/text/data/");
955 LocalArray
<Line
> lines(new Line
[200000]);
956 memset(lines
.getAlias(), 0, sizeof(Line
)*200000);
962 while (fgets(buffer
, 1024, testFile
) != NULL
) {
963 if(*buffer
== 0 || buffer
[0] == '#') {
964 // Store empty and comment lines so that errors are reported
965 // for the real test file lines.
966 lines
[lineNum
].buflen
= 0;
967 lines
[lineNum
].buff
[0] = 0;
969 int32_t buflen
= u_parseString(buffer
, bufferU
, 1024, &first
, &status
);
970 lines
[lineNum
].buflen
= buflen
;
971 u_memcpy(lines
[lineNum
].buff
, bufferU
, buflen
);
972 lines
[lineNum
].buff
[buflen
] = 0;
977 if(U_FAILURE(status
)) {
978 dataerrln("Couldn't read the test file!");
982 UVersionInfo uniVersion
;
983 static const UVersionInfo v62
= { 6, 2, 0, 0 };
984 u_getUnicodeVersion(uniVersion
);
985 UBool isAtLeastUCA62
= uprv_memcmp(uniVersion
, v62
, 4) >= 0;
987 LocalPointer
<Collator
> coll(Collator::createInstance(Locale::getRoot(), status
));
988 if(U_FAILURE(status
)) {
989 errcheckln(status
, "Couldn't open UCA collator");
992 coll
->setAttribute(UCOL_NORMALIZATION_MODE
, UCOL_ON
, status
);
993 coll
->setAttribute(UCOL_CASE_FIRST
, UCOL_OFF
, status
);
994 coll
->setAttribute(UCOL_CASE_LEVEL
, UCOL_OFF
, status
);
995 coll
->setAttribute(UCOL_STRENGTH
, isAtLeastUCA62
? UCOL_IDENTICAL
: UCOL_TERTIARY
, status
);
996 coll
->setAttribute(UCOL_ALTERNATE_HANDLING
, UCOL_NON_IGNORABLE
, status
);
998 int32_t spawnResult
= 0;
999 LocalArray
<CollatorThreadTest
> tests(new CollatorThreadTest
[kCollatorThreadThreads
]);
1001 logln(UnicodeString(u
"Spawning: ") + kCollatorThreadThreads
+ u
" threads * " + kFormatThreadIterations
+ u
" iterations each.");
1003 for(j
= 0; j
< kCollatorThreadThreads
; j
++) {
1004 //logln("Setting collator %i", j);
1005 tests
[j
].setCollator(coll
.getAlias(), lines
.getAlias(), lineNum
, isAtLeastUCA62
);
1007 for(j
= 0; j
< kCollatorThreadThreads
; j
++) {
1009 spawnResult
= tests
[j
].start();
1010 if(spawnResult
!= 0) {
1011 errln("%s:%d THREAD INFO: thread %d failed to start with status %d", __FILE__
, __LINE__
, j
, spawnResult
);
1015 logln("Spawned all");
1017 for(int32_t i
=0;i
<kCollatorThreadThreads
;i
++) {
1019 //logln(UnicodeString("Test #") + i + " is complete.. ");
1023 #endif /* #if !UCONFIG_NO_COLLATION */
1028 //-------------------------------------------------------------------------------------------
1030 // StringThreadTest2
1032 //-------------------------------------------------------------------------------------------
1034 const int kStringThreadIterations
= 2500;// # of iterations per thread
1035 const int kStringThreadThreads
= 10; // # of threads to spawn
1038 class StringThreadTest2
: public SimpleThread
1043 static const UnicodeString
*gSharedString
;
1045 StringThreadTest2() // constructor is NOT multithread safe.
1057 for (loopCount
= 0; loopCount
< kStringThreadIterations
; loopCount
++) {
1058 if (*gSharedString
!= u
"This is the original test string.") {
1059 IntlTest::gTest
->errln("%s:%d Original string is corrupt.", __FILE__
, __LINE__
);
1062 UnicodeString s1
= *gSharedString
;
1064 UnicodeString
s2(s1
);
1065 UnicodeString s3
= *gSharedString
;
1076 const UnicodeString
*StringThreadTest2::gSharedString
= NULL
;
1078 // ** The actual test function.
1081 void MultithreadTest::TestString()
1084 StringThreadTest2::gSharedString
= new UnicodeString(u
"This is the original test string.");
1085 StringThreadTest2 tests
[kStringThreadThreads
];
1087 logln(UnicodeString(u
"Spawning: ") + kStringThreadThreads
+ u
" threads * " + kStringThreadIterations
+ u
" iterations each.");
1088 for(j
= 0; j
< kStringThreadThreads
; j
++) {
1089 int32_t threadStatus
= tests
[j
].start();
1090 if (threadStatus
!= 0) {
1091 errln("%s:%d System Error %d starting thread number %d.", __FILE__
, __LINE__
, threadStatus
, j
);
1095 // Force a failure, to verify test is functioning and can report errors.
1096 // const_cast<UnicodeString *>(StringThreadTest2::gSharedString)->setCharAt(5, 'x');
1098 for(j
=0; j
<kStringThreadThreads
; j
++) {
1100 logln(UnicodeString(u
"Test #") + j
+ u
" is complete.. ");
1103 delete StringThreadTest2::gSharedString
;
1104 StringThreadTest2::gSharedString
= NULL
;
1109 // Test for ticket #10673, race in cache code in AnyTransliterator.
1110 // It's difficult to make the original unsafe code actually fail, but
1111 // this test will fairly reliably take the code path for races in
1112 // populating the cache.
1115 #if !UCONFIG_NO_TRANSLITERATION
1116 Transliterator
*gSharedTranslit
= NULL
;
1117 class TxThread
: public SimpleThread
{
1124 TxThread::~TxThread() {}
1125 void TxThread::run() {
1126 UnicodeString
greekString(u
"διαφορετικούς");
1127 gSharedTranslit
->transliterate(greekString
);
1128 IntlTest::gTest
->assertEquals(WHERE
, UnicodeString(u
"diaphoretikoús"), greekString
);
1133 void MultithreadTest::TestAnyTranslit() {
1134 #if !UCONFIG_NO_TRANSLITERATION
1135 UErrorCode status
= U_ZERO_ERROR
;
1136 LocalPointer
<Transliterator
> tx(Transliterator::createInstance("Any-Latin", UTRANS_FORWARD
, status
));
1137 if (!assertSuccess(WHERE
, status
, true)) { return; }
1139 gSharedTranslit
= tx
.getAlias();
1140 TxThread threads
[4];
1142 for (i
=0; i
<UPRV_LENGTHOF(threads
); i
++) {
1146 for (i
=0; i
<UPRV_LENGTHOF(threads
); i
++) {
1149 gSharedTranslit
= NULL
;
1150 #endif // !UCONFIG_NO_TRANSLITERATION
1155 // Condition Variables Test
1156 // Create a swarm of threads.
1157 // Using a mutex and a condition variables each thread
1158 // Increments a global count of started threads.
1159 // Broadcasts that it has started.
1160 // Waits on the condition that all threads have started.
1161 // Increments a global count of finished threads.
1162 // Waits on the condition that all threads have finished.
1166 class CondThread
: public SimpleThread
{
1168 CondThread() :fFinished(false) {};
1174 static UMutex
*gCTMutex() {
1175 static UMutex
*m
= STATIC_NEW(UMutex
);
1178 static std::condition_variable
*gCTConditionVar() {
1179 static std::condition_variable
*cv
= STATIC_NEW(std::condition_variable
);
1182 int gConditionTestOne
= 1; // Value one. Non-const, extern linkage to inhibit
1183 // compiler assuming a known value.
1184 int gStartedThreads
;
1185 int gFinishedThreads
;
1186 static const int NUMTHREADS
= 10;
1189 // Worker thread function.
1190 void CondThread::run() {
1191 std::unique_lock
<std::mutex
> lock(gCTMutex()->fMutex
);
1192 gStartedThreads
+= gConditionTestOne
;
1193 gCTConditionVar()->notify_all();
1195 while (gStartedThreads
< NUMTHREADS
) {
1196 if (gFinishedThreads
!= 0) {
1197 IntlTest::gTest
->errln("File %s, Line %d: Error, gStartedThreads = %d, gFinishedThreads = %d",
1198 __FILE__
, __LINE__
, gStartedThreads
, gFinishedThreads
);
1200 gCTConditionVar()->wait(lock
);
1203 gFinishedThreads
+= gConditionTestOne
;
1205 gCTConditionVar()->notify_all();
1207 while (gFinishedThreads
< NUMTHREADS
) {
1208 gCTConditionVar()->wait(lock
);
1212 void MultithreadTest::TestConditionVariables() {
1213 gStartedThreads
= 0;
1214 gFinishedThreads
= 0;
1216 CondThread
*threads
[NUMTHREADS
];
1219 std::unique_lock
<std::mutex
> lock(gCTMutex()->fMutex
);
1220 for (i
=0; i
<NUMTHREADS
; ++i
) {
1221 threads
[i
] = new CondThread
;
1222 threads
[i
]->start();
1225 while (gStartedThreads
< NUMTHREADS
) {
1226 gCTConditionVar()->wait(lock
);
1229 while (gFinishedThreads
< NUMTHREADS
) {
1230 gCTConditionVar()->wait(lock
);
1234 for (i
=0; i
<NUMTHREADS
; ++i
) {
1235 assertTrue(WHERE
, threads
[i
]->fFinished
);
1238 for (i
=0; i
<NUMTHREADS
; ++i
) {
1246 // Unified Cache Test
1249 // Each thread fetches a pair of objects. There are 8 distinct pairs:
1250 // ("en_US", "bs"), ("en_GB", "ca"), ("fr_FR", "ca_AD") etc.
1251 // These pairs represent 8 distinct languages
1253 // Note that only one value per language gets created in the cache.
1254 // In particular each cached value can have multiple keys.
1255 static const char *gCacheLocales
[] = {
1256 "en_US", "en_GB", "fr_FR", "fr",
1257 "de", "sr_ME", "sr_BA", "sr_CS"};
1258 static const char *gCacheLocales2
[] = {
1259 "bs", "ca", "ca_AD", "ca_ES",
1260 "en_US", "fi", "ff_CM", "ff_GN"};
1262 static int32_t gObjectsCreated
= 0; // protected by gCTMutex
1263 static const int32_t CACHE_LOAD
= 3;
1265 class UCTMultiThreadItem
: public SharedObject
{
1268 UCTMultiThreadItem(const char *x
) : value(NULL
) {
1269 value
= uprv_strdup(x
);
1271 virtual ~UCTMultiThreadItem() {
1279 const UCTMultiThreadItem
*LocaleCacheKey
<UCTMultiThreadItem
>::createObject(
1280 const void *context
, UErrorCode
&status
) const {
1281 const UnifiedCache
*cacheContext
= (const UnifiedCache
*) context
;
1283 if (uprv_strcmp(fLoc
.getLanguage(), fLoc
.getName()) != 0) {
1284 const UCTMultiThreadItem
*result
= NULL
;
1285 if (cacheContext
== NULL
) {
1286 UnifiedCache::getByLocale(fLoc
.getLanguage(), result
, status
);
1289 cacheContext
->get(LocaleCacheKey
<UCTMultiThreadItem
>(fLoc
.getLanguage()), result
, status
);
1293 bool firstObject
= false;
1295 std::unique_lock
<std::mutex
> lock(gCTMutex()->fMutex
);
1296 firstObject
= (gObjectsCreated
== 0);
1298 // Force the first object creation that comes through to wait
1299 // until other have completed. Verifies that cache doesn't
1300 // deadlock when a creation is slow.
1302 // Note that gObjectsCreated needs to be incremeneted from 0 to 1
1303 // early, to keep subsequent threads from entering this path.
1304 gObjectsCreated
= 1;
1305 while (gObjectsCreated
< 3) {
1306 gCTConditionVar()->wait(lock
);
1311 const UCTMultiThreadItem
*result
=
1312 new UCTMultiThreadItem(fLoc
.getLanguage());
1313 if (result
== NULL
) {
1314 status
= U_MEMORY_ALLOCATION_ERROR
;
1319 // Log that we created an object. The first object was already counted,
1320 // don't do it again.
1322 std::unique_lock
<std::mutex
> lock(gCTMutex()->fMutex
);
1324 gObjectsCreated
+= 1;
1326 gCTConditionVar()->notify_all();
1334 class UnifiedCacheThread
: public SimpleThread
{
1337 const UnifiedCache
*cache
,
1339 const char *loc2
) : fCache(cache
), fLoc(loc
), fLoc2(loc2
) {};
1340 ~UnifiedCacheThread() {};
1342 void exerciseByLocale(const Locale
&);
1343 const UnifiedCache
*fCache
;
1348 void UnifiedCacheThread::exerciseByLocale(const Locale
&locale
) {
1349 UErrorCode status
= U_ZERO_ERROR
;
1350 const UCTMultiThreadItem
*origItem
= NULL
;
1352 LocaleCacheKey
<UCTMultiThreadItem
>(locale
), fCache
, origItem
, status
);
1353 U_ASSERT(U_SUCCESS(status
));
1354 IntlTest::gTest
->assertEquals(WHERE
, locale
.getLanguage(), origItem
->value
);
1356 // Fetch the same item again many times. We should always get the same
1357 // pointer since this client is already holding onto it
1358 for (int32_t i
= 0; i
< 1000; ++i
) {
1359 const UCTMultiThreadItem
*item
= NULL
;
1361 LocaleCacheKey
<UCTMultiThreadItem
>(locale
), fCache
, item
, status
);
1362 IntlTest::gTest
->assertTrue(WHERE
, item
== origItem
);
1367 origItem
->removeRef();
1370 void UnifiedCacheThread::run() {
1371 // Run the exercise with 2 different locales so that we can exercise
1372 // eviction more. If each thread exercises just one locale, then
1373 // eviction can't start until the threads end.
1374 exerciseByLocale(fLoc
);
1375 exerciseByLocale(fLoc2
);
1378 void MultithreadTest::TestUnifiedCache() {
1380 // Start with our own local cache so that we have complete control
1381 // and set the eviction policy to evict starting with 2 unused
1383 UErrorCode status
= U_ZERO_ERROR
;
1384 UnifiedCache::getInstance(status
);
1385 UnifiedCache
cache(status
);
1386 cache
.setEvictionPolicy(2, 0, status
);
1387 U_ASSERT(U_SUCCESS(status
));
1389 gFinishedThreads
= 0;
1390 gObjectsCreated
= 0;
1392 UnifiedCacheThread
*threads
[CACHE_LOAD
][UPRV_LENGTHOF(gCacheLocales
)];
1393 for (int32_t i
=0; i
<CACHE_LOAD
; ++i
) {
1394 for (int32_t j
=0; j
<UPRV_LENGTHOF(gCacheLocales
); ++j
) {
1395 // Each thread works with a pair of locales.
1396 threads
[i
][j
] = new UnifiedCacheThread(
1397 &cache
, gCacheLocales
[j
], gCacheLocales2
[j
]);
1398 threads
[i
][j
]->start();
1402 for (int32_t i
=0; i
<CACHE_LOAD
; ++i
) {
1403 for (int32_t j
=0; j
<UPRV_LENGTHOF(gCacheLocales
); ++j
) {
1404 threads
[i
][j
]->join();
1407 // Because of cache eviction, we can't assert exactly how many
1408 // distinct objects get created over the course of this run.
1409 // However we know that at least 8 objects get created because that
1410 // is how many distinct languages we have in our test.
1411 if (gObjectsCreated
< 8) {
1412 errln("%s:%d Too few objects created.", __FILE__
, __LINE__
);
1414 // We know that each thread cannot create more than 2 objects in
1415 // the cache, and there are UPRV_LENGTHOF(gCacheLocales) pairs of
1416 // objects fetched from the cache. If the threads run in series because
1417 // of eviction, at worst case each thread creates two objects.
1418 if (gObjectsCreated
> 2 * CACHE_LOAD
* UPRV_LENGTHOF(gCacheLocales
)) {
1419 errln("%s:%d Too many objects created, got %d, expected %d", __FILE__
, __LINE__
, gObjectsCreated
, 2 * CACHE_LOAD
* UPRV_LENGTHOF(gCacheLocales
));
1423 assertEquals(WHERE
, 2, cache
.unusedCount());
1426 for (int32_t i
=0; i
<CACHE_LOAD
; ++i
) {
1427 for (int32_t j
=0; j
<UPRV_LENGTHOF(gCacheLocales
); ++j
) {
1428 delete threads
[i
][j
];
1433 #if !UCONFIG_NO_TRANSLITERATION
1435 // BreakTransliterator Threading Test
1436 // This is a test for bug #11603. Test verified to fail prior to fix.
1439 static const Transliterator
*gSharedTransliterator
;
1440 static const UnicodeString
*gTranslitInput
;
1441 static const UnicodeString
*gTranslitExpected
;
1443 class BreakTranslitThread
: public SimpleThread
{
1445 BreakTranslitThread() {};
1446 ~BreakTranslitThread() {};
1450 void BreakTranslitThread::run() {
1451 for (int i
=0; i
<10; i
++) {
1452 icu::UnicodeString
s(*gTranslitInput
);
1453 gSharedTransliterator
->transliterate(s
);
1454 if (*gTranslitExpected
!= s
) {
1455 IntlTest::gTest
->errln("%s:%d Transliteration threading failure.", __FILE__
, __LINE__
);
1461 void MultithreadTest::TestBreakTranslit() {
1462 UErrorCode status
= U_ZERO_ERROR
;
1463 UnicodeString
input(
1464 u
"\u0E42\u0E14\u0E22\u0E1E\u0E37\u0E49\u0E19\u0E10\u0E32\u0E19\u0E41\u0E25\u0E49\u0E27,");
1465 // Thai script, โดยพื้นฐานแล้ว
1466 gTranslitInput
= &input
;
1468 gSharedTransliterator
= Transliterator::createInstance(
1469 UnicodeString(u
"Any-Latin; Lower; NFD; [:Diacritic:]Remove; NFC; Latin-ASCII;"), UTRANS_FORWARD
, status
);
1470 assertSuccess(WHERE
, status
);
1471 if (!assertTrue(WHERE
, gSharedTransliterator
!= nullptr)) {
1475 UnicodeString
expected(*gTranslitInput
);
1476 gSharedTransliterator
->transliterate(expected
);
1477 gTranslitExpected
= &expected
;
1479 BreakTranslitThread threads
[4];
1480 for (int i
=0; i
<UPRV_LENGTHOF(threads
); ++i
) {
1483 for (int i
=0; i
<UPRV_LENGTHOF(threads
); ++i
) {
1487 delete gSharedTransliterator
;
1488 gTranslitInput
= NULL
;
1489 gTranslitExpected
= NULL
;
1493 class TestIncDecThread
: public SimpleThread
{
1495 TestIncDecThread() { };
1499 static u_atomic_int32_t gIncDecCounter
;
1501 void TestIncDecThread::run() {
1502 umtx_atomic_inc(&gIncDecCounter
);
1503 for (int32_t i
=0; i
<5000000; ++i
) {
1504 umtx_atomic_inc(&gIncDecCounter
);
1505 umtx_atomic_dec(&gIncDecCounter
);
1509 void MultithreadTest::TestIncDec()
1511 static constexpr int NUM_THREADS
= 4;
1513 TestIncDecThread threads
[NUM_THREADS
];
1514 for (auto &thread
:threads
) {
1517 for (auto &thread
:threads
) {
1520 assertEquals(WHERE
, NUM_THREADS
, gIncDecCounter
);
1523 #if !UCONFIG_NO_FORMATTING
1524 static Calendar
*gSharedCalendar
= {};
1526 class Test20104Thread
: public SimpleThread
{
1528 Test20104Thread() { };
1532 void Test20104Thread::run() {
1533 gSharedCalendar
->defaultCenturyStartYear();
1536 void MultithreadTest::Test20104() {
1537 UErrorCode status
= U_ZERO_ERROR
;
1538 Locale
loc("hi_IN");
1539 gSharedCalendar
= new IndianCalendar(loc
, status
);
1540 assertSuccess(WHERE
, status
);
1542 static constexpr int NUM_THREADS
= 4;
1543 Test20104Thread threads
[NUM_THREADS
];
1544 for (auto &thread
:threads
) {
1547 for (auto &thread
:threads
) {
1550 delete gSharedCalendar
;
1551 // Note: failure is reported by Thread Sanitizer. Test itself succeeds.
1553 #endif /* !UCONFIG_NO_FORMATTING */
1555 #endif /* !UCONFIG_NO_TRANSLITERATION */