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"
17 #include "unicode/localpointer.h"
18 #include "unicode/resbund.h"
19 #include "unicode/udata.h"
20 #include "unicode/uloc.h"
21 #include "unicode/locid.h"
25 #include "unicode/ushape.h"
26 #include "unicode/translit.h"
27 #include "sharedobject.h"
28 #include "unifiedcache.h"
32 #define TSMTHREAD_FAIL(msg) errln("%s at file %s, line %d", msg, __FILE__, __LINE__)
33 #define TSMTHREAD_ASSERT(expr) {if (!(expr)) {TSMTHREAD_FAIL("Fail");}}
34 #define TSMTHREAD_ASSERT_SUCCESS(status) {if (U_FAILURE(status)) { \
35 errln("file: %s:%d status = %s\n", __FILE__, __LINE__, u_errorName(status));}}
37 MultithreadTest::MultithreadTest()
41 MultithreadTest::~MultithreadTest()
47 #include <ctype.h> // tolower, toupper
50 #include "unicode/putil.h"
53 #include "unicode/numfmt.h"
54 #include "unicode/choicfmt.h"
55 #include "unicode/msgfmt.h"
56 #include "unicode/locid.h"
57 #include "unicode/coll.h"
58 #include "unicode/calendar.h"
62 void MultithreadTest::runIndexedTest( int32_t index
, UBool exec
,
63 const char* &name
, char* /*par*/ ) {
65 logln("TestSuite MultithreadTest: ");
68 TESTCASE_AUTO(TestThreads
);
69 TESTCASE_AUTO(TestMutex
);
70 #if !UCONFIG_NO_FORMATTING
71 TESTCASE_AUTO(TestThreadedIntl
);
73 #if !UCONFIG_NO_COLLATION
74 TESTCASE_AUTO(TestCollators
);
75 #endif /* #if !UCONFIG_NO_COLLATION */
76 TESTCASE_AUTO(TestString
);
77 TESTCASE_AUTO(TestArabicShapingThreads
);
78 TESTCASE_AUTO(TestAnyTranslit
);
79 TESTCASE_AUTO(TestConditionVariables
);
80 TESTCASE_AUTO(TestUnifiedCache
);
81 #if !UCONFIG_NO_TRANSLITERATION
82 TESTCASE_AUTO(TestBreakTranslit
);
83 TESTCASE_AUTO(TestIncDec
);
84 #endif /* #if !UCONFIG_NO_TRANSLITERATION */
89 //-----------------------------------------------------------------------------------
91 // TestThreads -- see if threads really work at all.
93 // Set up N threads pointing at N chars. When they are started, they will
94 // set their chars. At the end we make sure they are all set.
96 //-----------------------------------------------------------------------------------
98 class TestThreadsThread
: public SimpleThread
101 TestThreadsThread(char* whatToChange
) { fWhatToChange
= whatToChange
; }
102 virtual void run() { Mutex m
;
103 *fWhatToChange
= '*';
110 void MultithreadTest::TestThreads()
112 static const int32_t THREADTEST_NRTHREADS
= 8;
113 char threadTestChars
[THREADTEST_NRTHREADS
+ 1];
114 SimpleThread
*threads
[THREADTEST_NRTHREADS
];
115 int32_t numThreadsStarted
= 0;
118 for(i
=0;i
<THREADTEST_NRTHREADS
;i
++)
120 threadTestChars
[i
] = ' ';
121 threads
[i
] = new TestThreadsThread(&threadTestChars
[i
]);
123 threadTestChars
[THREADTEST_NRTHREADS
] = '\0';
125 logln("->" + UnicodeString(threadTestChars
) + "<- Firing off threads.. ");
126 for(i
=0;i
<THREADTEST_NRTHREADS
;i
++)
128 if (threads
[i
]->start() != 0) {
129 errln("Error starting thread %d", i
);
134 logln(" Subthread started.");
137 if (numThreadsStarted
!= THREADTEST_NRTHREADS
) {
138 errln("Not all threads could be started for testing!");
142 logln("Waiting for threads to be set..");
143 for(i
=0; i
<THREADTEST_NRTHREADS
; i
++) {
145 if (threadTestChars
[i
] != '*') {
146 errln("%s:%d Thread %d failed.", __FILE__
, __LINE__
, i
);
153 //-----------------------------------------------------------------------------------
155 // TestArabicShapeThreads -- see if calls to u_shapeArabic in many threads works successfully
157 // Set up N threads pointing at N chars. When they are started, they will make calls to doTailTest which tests
158 // u_shapeArabic, if the calls are successful it will the set * chars.
159 // At the end we make sure all threads managed to run u_shapeArabic successfully.
160 // This is a unit test for ticket 9473
162 //-----------------------------------------------------------------------------------
164 class TestArabicShapeThreads
: public SimpleThread
167 TestArabicShapeThreads() {};
168 virtual void run() { doTailTest(); };
174 void TestArabicShapeThreads::doTailTest(void) {
175 static const UChar src
[] = { 0x0020, 0x0633, 0 };
176 static const UChar dst_old
[] = { 0xFEB1, 0x200B,0 };
177 static const UChar dst_new
[] = { 0xFEB1, 0xFE73,0 };
178 UChar dst
[3] = { 0x0000, 0x0000,0 };
182 for (int32_t loopCount
= 0; loopCount
< 100; loopCount
++) {
183 status
= U_ZERO_ERROR
;
184 length
= u_shapeArabic(src
, -1, dst
, UPRV_LENGTHOF(dst
),
185 U_SHAPE_LETTERS_SHAPE
|U_SHAPE_SEEN_TWOCELL_NEAR
, &status
);
186 if(U_FAILURE(status
)) {
187 IntlTest::gTest
->errln("Fail: status %s\n", u_errorName(status
));
189 } else if(length
!=2) {
190 IntlTest::gTest
->errln("Fail: len %d expected 3\n", length
);
192 } else if(u_strncmp(dst
,dst_old
,UPRV_LENGTHOF(dst
))) {
193 IntlTest::gTest
->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
194 dst
[0],dst
[1],dst_old
[0],dst_old
[1]);
200 status
= U_ZERO_ERROR
;
201 length
= u_shapeArabic(src
, -1, dst
, UPRV_LENGTHOF(dst
),
202 U_SHAPE_LETTERS_SHAPE
|U_SHAPE_SEEN_TWOCELL_NEAR
|U_SHAPE_TAIL_NEW_UNICODE
, &status
);
203 if(U_FAILURE(status
)) {
204 IntlTest::gTest
->errln("Fail: status %s\n", u_errorName(status
));
206 } else if(length
!=2) {
207 IntlTest::gTest
->errln("Fail: len %d expected 3\n", length
);
209 } else if(u_strncmp(dst
,dst_new
,UPRV_LENGTHOF(dst
))) {
210 IntlTest::gTest
->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
211 dst
[0],dst
[1],dst_new
[0],dst_new
[1]);
219 void MultithreadTest::TestArabicShapingThreads()
221 TestArabicShapeThreads threads
[30];
225 logln("-> do TestArabicShapingThreads <- Firing off threads.. ");
226 for(i
=0; i
< UPRV_LENGTHOF(threads
); i
++) {
227 if (threads
[i
].start() != 0) {
228 errln("Error starting thread %d", i
);
232 for(i
=0; i
< UPRV_LENGTHOF(threads
); i
++) {
235 logln("->TestArabicShapingThreads <- Got all threads! cya");
239 //-----------------------------------------------------------------------
241 // TestMutex - a simple (non-stress) test to verify that ICU mutexes
242 // and condition variables are functioning. Does not test the use of
243 // mutexes within ICU services, but rather that the
244 // platform's mutex support is at least superficially there.
246 //----------------------------------------------------------------------
247 static UMutex gTestMutexA
= U_MUTEX_INITIALIZER
;
248 static UConditionVar gThreadsCountChanged
= U_CONDITION_INITIALIZER
;
250 static int gThreadsStarted
= 0;
251 static int gThreadsInMiddle
= 0;
252 static int gThreadsDone
= 0;
254 static const int TESTMUTEX_THREAD_COUNT
= 40;
256 class TestMutexThread
: public SimpleThread
260 // This is the code that each of the spawned threads runs.
261 // All threads move together throught the started - middle - done sequence together,
262 // waiting for all other threads to reach each point before advancing.
263 umtx_lock(&gTestMutexA
);
264 gThreadsStarted
+= 1;
265 umtx_condBroadcast(&gThreadsCountChanged
);
266 while (gThreadsStarted
< TESTMUTEX_THREAD_COUNT
) {
267 if (gThreadsInMiddle
!= 0) {
268 IntlTest::gTest
->errln(
269 "%s:%d gThreadsInMiddle = %d. Expected 0.", __FILE__
, __LINE__
, gThreadsInMiddle
);
272 umtx_condWait(&gThreadsCountChanged
, &gTestMutexA
);
275 gThreadsInMiddle
+= 1;
276 umtx_condBroadcast(&gThreadsCountChanged
);
277 while (gThreadsInMiddle
< TESTMUTEX_THREAD_COUNT
) {
278 if (gThreadsDone
!= 0) {
279 IntlTest::gTest
->errln(
280 "%s:%d gThreadsDone = %d. Expected 0.", __FILE__
, __LINE__
, gThreadsDone
);
283 umtx_condWait(&gThreadsCountChanged
, &gTestMutexA
);
287 umtx_condBroadcast(&gThreadsCountChanged
);
288 while (gThreadsDone
< TESTMUTEX_THREAD_COUNT
) {
289 umtx_condWait(&gThreadsCountChanged
, &gTestMutexA
);
291 umtx_unlock(&gTestMutexA
);
295 void MultithreadTest::TestMutex()
298 gThreadsInMiddle
= 0;
301 TestMutexThread threads
[TESTMUTEX_THREAD_COUNT
];
302 umtx_lock(&gTestMutexA
);
303 for (i
=0; i
<TESTMUTEX_THREAD_COUNT
; i
++) {
304 if (threads
[i
].start() != 0) {
305 errln("%s:%d Error starting thread %d", __FILE__
, __LINE__
, i
);
310 // Because we are holding gTestMutexA, all of the threads should be blocked
311 // at the start of their run() function.
312 if (gThreadsStarted
!= 0) {
313 errln("%s:%d gThreadsStarted=%d. Expected 0.", __FILE__
, __LINE__
, gThreadsStarted
);
317 while (gThreadsInMiddle
< TESTMUTEX_THREAD_COUNT
) {
318 if (gThreadsDone
!= 0) {
319 errln("%s:%d gThreadsDone=%d. Expected 0.", __FILE__
, __LINE__
, gThreadsStarted
);
322 umtx_condWait(&gThreadsCountChanged
, &gTestMutexA
);
325 while (gThreadsDone
< TESTMUTEX_THREAD_COUNT
) {
326 umtx_condWait(&gThreadsCountChanged
, &gTestMutexA
);
328 umtx_unlock(&gTestMutexA
);
330 for (i
=0; i
<TESTMUTEX_THREAD_COUNT
; i
++) {
336 //-------------------------------------------------------------------------------------------
338 // TestMultithreadedIntl. Test ICU Formatting in a multi-threaded environment
340 //-------------------------------------------------------------------------------------------
343 // * Show exactly where the string's differences lie.
344 UnicodeString
showDifference(const UnicodeString
& expected
, const UnicodeString
& result
)
347 res
= expected
+ "<Expected\n";
348 if(expected
.length() != result
.length())
349 res
+= " [ Different lengths ] \n";
352 for(int32_t i
=0;i
<expected
.length();i
++)
354 if(expected
[i
] == result
[i
])
363 res
+= "<Differences";
366 res
+= result
+ "<Result\n";
372 //-------------------------------------------------------------------------------------------
374 // FormatThreadTest - a thread that tests performing a number of numberformats.
376 //-------------------------------------------------------------------------------------------
378 const int kFormatThreadIterations
= 100; // # of iterations per thread
379 const int kFormatThreadThreads
= 10; // # of threads to spawn
381 #if !UCONFIG_NO_FORMATTING
385 struct FormatThreadTestData
388 UnicodeString string
;
389 FormatThreadTestData(double a
, const UnicodeString
& b
) : number(a
),string(b
) {}
393 // "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3 number,currency}."
395 static void formatErrorMessage(UErrorCode
&realStatus
, const UnicodeString
& pattern
, const Locale
& theLocale
,
396 UErrorCode inStatus0
, // statusString 1
397 const Locale
&inCountry2
, double currency3
, // these numbers are the message arguments.
398 UnicodeString
&result
)
400 if(U_FAILURE(realStatus
))
401 return; // you messed up
403 UnicodeString
errString1(u_errorName(inStatus0
));
405 UnicodeString countryName2
;
406 inCountry2
.getDisplayCountry(theLocale
,countryName2
);
408 Formattable myArgs
[] = {
409 Formattable((int32_t)inStatus0
), // inStatus0 {0}
410 Formattable(errString1
), // statusString1 {1}
411 Formattable(countryName2
), // inCountry2 {2}
412 Formattable(currency3
)// currency3 {3,number,currency}
415 MessageFormat
*fmt
= new MessageFormat("MessageFormat's API is broken!!!!!!!!!!!",realStatus
);
416 fmt
->setLocale(theLocale
);
417 fmt
->applyPattern(pattern
, realStatus
);
419 if (U_FAILURE(realStatus
)) {
424 FieldPosition ignore
= 0;
425 fmt
->format(myArgs
,4,result
,ignore
,realStatus
);
431 * Shared formatters & data used by instances of ThreadSafeFormat.
432 * Exactly one instance of this class is created, and it is then shared concurrently
433 * by the multiple instances of ThreadSafeFormat.
435 class ThreadSafeFormatSharedData
{
437 ThreadSafeFormatSharedData(UErrorCode
&status
);
438 ~ThreadSafeFormatSharedData();
439 LocalPointer
<NumberFormat
> fFormat
;
440 Formattable fYDDThing
;
441 Formattable fBBDThing
;
442 UnicodeString fYDDStr
;
443 UnicodeString fBBDStr
;
446 const ThreadSafeFormatSharedData
*gSharedData
= NULL
;
448 ThreadSafeFormatSharedData::ThreadSafeFormatSharedData(UErrorCode
&status
) {
449 fFormat
.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status
));
450 static const UChar kYDD
[] = { 0x59, 0x44, 0x44, 0x00 };
451 static const UChar kBBD
[] = { 0x42, 0x42, 0x44, 0x00 };
452 fYDDThing
.adoptObject(new CurrencyAmount(123.456, kYDD
, status
));
453 fBBDThing
.adoptObject(new CurrencyAmount(987.654, kBBD
, status
));
454 if (U_FAILURE(status
)) {
457 fFormat
->format(fYDDThing
, fYDDStr
, NULL
, status
);
458 fFormat
->format(fBBDThing
, fBBDStr
, NULL
, status
);
462 ThreadSafeFormatSharedData::~ThreadSafeFormatSharedData() {
467 * Class for thread-safe testing of format.
468 * Instances of this class appear as members of class FormatThreadTest.
469 * Multiple instances of FormatThreadTest coexist.
470 * ThreadSafeFormat::doStuff() is called concurrently to test the thread safety of
471 * various shared format operations.
473 class ThreadSafeFormat
{
475 /* give a unique offset to each thread */
476 ThreadSafeFormat(UErrorCode
&status
);
477 UBool
doStuff(int32_t offset
, UnicodeString
&appendErr
, UErrorCode
&status
) const;
479 LocalPointer
<NumberFormat
> fFormat
; // formatter - en_US constructed currency
483 ThreadSafeFormat::ThreadSafeFormat(UErrorCode
&status
) {
484 fFormat
.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status
));
487 static const UChar kUSD
[] = { 0x55, 0x53, 0x44, 0x00 };
489 UBool
ThreadSafeFormat::doStuff(int32_t offset
, UnicodeString
&appendErr
, UErrorCode
&status
) const {
492 if(u_strcmp(fFormat
->getCurrency(), kUSD
)) {
493 appendErr
.append("fFormat currency != ")
496 .append(fFormat
->getCurrency())
501 if(u_strcmp(gSharedData
->fFormat
->getCurrency(), kUSD
)) {
502 appendErr
.append("gFormat currency != ")
505 .append(gSharedData
->fFormat
->getCurrency())
510 const UnicodeString
*o
=NULL
;
512 const NumberFormat
*nf
= NULL
; // only operate on it as const.
514 case 0: f
= gSharedData
->fYDDThing
; o
= &gSharedData
->fYDDStr
; nf
= gSharedData
->fFormat
.getAlias(); break;
515 case 1: f
= gSharedData
->fBBDThing
; o
= &gSharedData
->fBBDStr
; nf
= gSharedData
->fFormat
.getAlias(); break;
516 case 2: f
= gSharedData
->fYDDThing
; o
= &gSharedData
->fYDDStr
; nf
= fFormat
.getAlias(); break;
517 case 3: f
= gSharedData
->fBBDThing
; o
= &gSharedData
->fBBDStr
; nf
= fFormat
.getAlias(); break;
519 nf
->format(f
, str
, NULL
, status
);
522 appendErr
.append(showDifference(*o
, str
));
528 UBool U_CALLCONV
isAcceptable(void *, const char *, const char *, const UDataInfo
*) {
532 //static UMTX debugMutex = NULL;
533 //static UMTX gDebugMutex;
536 class FormatThreadTest
: public SimpleThread
542 LocalPointer
<ThreadSafeFormat
> fTSF
;
544 FormatThreadTest() // constructor is NOT multithread safe.
552 UErrorCode status
= U_ZERO_ERROR
; // TODO: rearrange code to allow checking of status.
553 fTSF
.adoptInstead(new ThreadSafeFormat(status
));
554 static int32_t fgOffset
= 0;
563 LocalPointer
<NumberFormat
> percentFormatter
;
564 UErrorCode status
= U_ZERO_ERROR
;
568 for (int i
=0; i
<4000; i
++) {
569 status
= U_ZERO_ERROR
;
570 UDataMemory
*data1
= udata_openChoice(0, "res", "en_US", isAcceptable
, 0, &status
);
571 UDataMemory
*data2
= udata_openChoice(0, "res", "fr", isAcceptable
, 0, &status
);
574 if (U_FAILURE(status
)) {
575 error("udata_openChoice failed.\n");
585 for (m
=0; m
<4000; m
++) {
586 status
= U_ZERO_ERROR
;
587 UResourceBundle
*res
= NULL
;
588 const char *localeName
= NULL
;
590 Locale loc
= Locale::getEnglish();
592 localeName
= loc
.getName();
593 // localeName = "en";
595 // ResourceBundle bund = ResourceBundle(0, loc, status);
596 //umtx_lock(&gDebugMutex);
597 res
= ures_open(NULL
, localeName
, &status
);
598 //umtx_unlock(&gDebugMutex);
600 //umtx_lock(&gDebugMutex);
602 //umtx_unlock(&gDebugMutex);
604 if (U_FAILURE(status
)) {
605 error("Resource bundle construction failed.\n");
612 // Keep this data here to avoid static initialization.
613 FormatThreadTestData kNumberFormatTestData
[] =
615 FormatThreadTestData((double)5.0, UnicodeString(u
"5")),
616 FormatThreadTestData( 6.0, UnicodeString(u
"6")),
617 FormatThreadTestData( 20.0, UnicodeString(u
"20")),
618 FormatThreadTestData( 8.0, UnicodeString(u
"8")),
619 FormatThreadTestData( 8.3, UnicodeString(u
"8.3")),
620 FormatThreadTestData( 12345, UnicodeString(u
"12,345")),
621 FormatThreadTestData( 81890.23, UnicodeString(u
"81,890.23")),
623 int32_t kNumberFormatTestDataLength
= UPRV_LENGTHOF(kNumberFormatTestData
);
625 // Keep this data here to avoid static initialization.
626 FormatThreadTestData kPercentFormatTestData
[] =
628 FormatThreadTestData((double)5.0, CharsToUnicodeString("500\\u00a0%")),
629 FormatThreadTestData( 1.0, CharsToUnicodeString("100\\u00a0%")),
630 FormatThreadTestData( 0.26, CharsToUnicodeString("26\\u00a0%")),
631 FormatThreadTestData(
632 16384.99, CharsToUnicodeString("1\\u00a0638\\u00a0499\\u00a0%")), // U+00a0 = NBSP
633 FormatThreadTestData(
634 81890.23, CharsToUnicodeString("8\\u00a0189\\u00a0023\\u00a0%")),
636 int32_t kPercentFormatTestDataLength
= UPRV_LENGTHOF(kPercentFormatTestData
);
639 status
= U_ZERO_ERROR
;
640 LocalPointer
<NumberFormat
> formatter(NumberFormat::createInstance(Locale::getEnglish(),status
));
641 if(U_FAILURE(status
)) {
642 IntlTest::gTest
->dataerrln("%s:%d Error %s on NumberFormat::createInstance().",
643 __FILE__
, __LINE__
, u_errorName(status
));
644 goto cleanupAndReturn
;
647 percentFormatter
.adoptInstead(NumberFormat::createPercentInstance(Locale::getFrench(),status
));
648 if(U_FAILURE(status
)) {
649 IntlTest::gTest
->errln("%s:%d Error %s on NumberFormat::createPercentInstance().",
650 __FILE__
, __LINE__
, u_errorName(status
));
651 goto cleanupAndReturn
;
654 for(iteration
= 0;!IntlTest::gTest
->getErrors() && iteration
<kFormatThreadIterations
;iteration
++)
657 int32_t whichLine
= (iteration
+ fOffset
)%kNumberFormatTestDataLength
;
659 UnicodeString output
;
661 formatter
->format(kNumberFormatTestData
[whichLine
].number
, output
);
663 if(0 != output
.compare(kNumberFormatTestData
[whichLine
].string
)) {
664 IntlTest::gTest
->errln("format().. expected " + kNumberFormatTestData
[whichLine
].string
666 goto cleanupAndReturn
;
669 // Now check percent.
671 whichLine
= (iteration
+ fOffset
)%kPercentFormatTestDataLength
;
673 percentFormatter
->format(kPercentFormatTestData
[whichLine
].number
, output
);
674 if(0 != output
.compare(kPercentFormatTestData
[whichLine
].string
))
676 IntlTest::gTest
->errln("percent format().. \n" +
677 showDifference(kPercentFormatTestData
[whichLine
].string
,output
));
678 goto cleanupAndReturn
;
681 // Test message error
682 const int kNumberOfMessageTests
= 3;
683 UErrorCode statusToCheck
;
684 UnicodeString patternToCheck
;
685 Locale messageLocale
;
686 Locale countryToCheck
;
687 double currencyToCheck
;
689 UnicodeString expected
;
692 switch((iteration
+fOffset
) % kNumberOfMessageTests
)
696 statusToCheck
= U_FILE_ACCESS_ERROR
;
697 patternToCheck
= "0:Someone from {2} is receiving a #{0}"
698 " error - {1}. Their telephone call is costing "
699 "{3,number,currency}."; // number,currency
700 messageLocale
= Locale("en","US");
701 countryToCheck
= Locale("","HR");
702 currencyToCheck
= 8192.77;
703 expected
= "0:Someone from Croatia is receiving a #4 error - "
704 "U_FILE_ACCESS_ERROR. Their telephone call is costing $8,192.77.";
707 statusToCheck
= U_INDEX_OUTOFBOUNDS_ERROR
;
708 patternToCheck
= "1:A customer in {2} is receiving a #{0} error - {1}. "
709 "Their telephone call is costing {3,number,currency}."; // number,currency
710 messageLocale
= Locale("de","DE@currency=DEM");
711 countryToCheck
= Locale("","BF");
712 currencyToCheck
= 2.32;
713 expected
= CharsToUnicodeString(
714 "1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. "
715 "Their telephone call is costing 2,32\\u00A0DM.");
718 statusToCheck
= U_MEMORY_ALLOCATION_ERROR
;
719 patternToCheck
= "2:user in {2} is receiving a #{0} error - {1}. "
720 "They insist they just spent {3,number,currency} "
721 "on memory."; // number,currency
722 messageLocale
= Locale("de","AT@currency=ATS"); // Austrian German
723 countryToCheck
= Locale("","US"); // hmm
724 currencyToCheck
= 40193.12;
725 expected
= CharsToUnicodeString(
726 "2:user in Vereinigte Staaten is receiving a #7 error"
727 " - U_MEMORY_ALLOCATION_ERROR. They insist they just spent"
728 " \\u00f6S\\u00A040.193,12 on memory.");
732 UnicodeString result
;
733 UErrorCode status
= U_ZERO_ERROR
;
734 formatErrorMessage(status
,patternToCheck
,messageLocale
,statusToCheck
,
735 countryToCheck
,currencyToCheck
,result
);
736 if(U_FAILURE(status
))
738 UnicodeString
tmp(u_errorName(status
));
739 IntlTest::gTest
->errln("Failure on message format, pattern=" + patternToCheck
+
741 goto cleanupAndReturn
;
744 if(result
!= expected
)
746 IntlTest::gTest
->errln("PatternFormat: \n" + showDifference(expected
,result
));
747 goto cleanupAndReturn
;
749 // test the Thread Safe Format
750 UnicodeString appendErr
;
751 if(!fTSF
->doStuff(fNum
, appendErr
, status
)) {
752 IntlTest::gTest
->errln(appendErr
);
753 goto cleanupAndReturn
;
755 } /* end of for loop */
764 int32_t fOffset
; // where we are testing from.
767 // ** The actual test function.
769 void MultithreadTest::TestThreadedIntl()
771 UnicodeString theErr
;
773 UErrorCode threadSafeErr
= U_ZERO_ERROR
;
775 ThreadSafeFormatSharedData
sharedData(threadSafeErr
);
776 assertSuccess("initializing ThreadSafeFormat", threadSafeErr
, TRUE
);
779 // Create and start the test threads
781 logln("Spawning: %d threads * %d iterations each.",
782 kFormatThreadThreads
, kFormatThreadIterations
);
783 FormatThreadTest tests
[kFormatThreadThreads
];
785 for(j
= 0; j
< UPRV_LENGTHOF(tests
); j
++) {
787 int32_t threadStatus
= tests
[j
].start();
788 if (threadStatus
!= 0) {
789 errln("%s:%d System Error %d starting thread number %d.",
790 __FILE__
, __LINE__
, threadStatus
, j
);
796 for (j
=0; j
<UPRV_LENGTHOF(tests
); j
++) {
798 logln("Thread # %d is complete..", j
);
801 #endif /* #if !UCONFIG_NO_FORMATTING */
807 //-------------------------------------------------------------------------------------------
809 // Collation threading test
811 //-------------------------------------------------------------------------------------------
812 #if !UCONFIG_NO_COLLATION
814 #define kCollatorThreadThreads 10 // # of threads to spawn
815 #define kCollatorThreadPatience kCollatorThreadThreads*30
823 skipLineBecauseOfBug(const UChar
*s
, int32_t length
) {
824 // TODO: Fix ICU ticket #8052
826 (s
[0] == 0xfb2 || s
[0] == 0xfb3) &&
828 (s
[2] == 0xf73 || s
[2] == 0xf75 || s
[2] == 0xf81)) {
834 static UCollationResult
835 normalizeResult(int32_t result
) {
836 return result
<0 ? UCOL_LESS
: result
==0 ? UCOL_EQUAL
: UCOL_GREATER
;
839 class CollatorThreadTest
: public SimpleThread
842 const Collator
*coll
;
845 UBool isAtLeastUCA62
;
847 CollatorThreadTest() : SimpleThread(),
854 void setCollator(Collator
*c
, Line
*l
, int32_t nl
, UBool atLeastUCA62
)
859 isAtLeastUCA62
= atLeastUCA62
;
862 uint8_t sk1
[1024], sk2
[1024];
863 uint8_t *oldSk
= NULL
, *newSk
= sk1
;
868 for(i
= 0; i
< noLines
; i
++) {
869 if(lines
[i
].buflen
== 0) { continue; }
871 if(skipLineBecauseOfBug(lines
[i
].buff
, lines
[i
].buflen
)) { continue; }
873 int32_t resLen
= coll
->getSortKey(lines
[i
].buff
, lines
[i
].buflen
, newSk
, 1024);
876 int32_t skres
= strcmp((char *)oldSk
, (char *)newSk
);
877 int32_t cmpres
= coll
->compare(lines
[prev
].buff
, lines
[prev
].buflen
, lines
[i
].buff
, lines
[i
].buflen
);
878 int32_t cmpres2
= coll
->compare(lines
[i
].buff
, lines
[i
].buflen
, lines
[prev
].buff
, lines
[prev
].buflen
);
880 if(cmpres
!= -cmpres2
) {
881 IntlTest::gTest
->errln(UnicodeString("Compare result not symmetrical on line ") + (i
+ 1));
885 if(cmpres
!= normalizeResult(skres
)) {
886 IntlTest::gTest
->errln(UnicodeString("Difference between coll->compare and sortkey compare on line ") + (i
+ 1));
890 int32_t res
= cmpres
;
891 if(res
== 0 && !isAtLeastUCA62
) {
892 // Up to UCA 6.1, the collation test files use a custom tie-breaker,
893 // comparing the raw input strings.
894 res
= u_strcmpCodePointOrder(lines
[prev
].buff
, lines
[i
].buff
);
895 // Starting with UCA 6.2, the collation test files use the standard UCA tie-breaker,
896 // comparing the NFD versions of the input strings,
897 // which we do via setting strength=identical.
900 IntlTest::gTest
->errln(UnicodeString("Line is not greater or equal than previous line, for line ") + (i
+ 1));
907 (void)oldLen
; // Suppress set but not used warning.
910 newSk
= (newSk
== sk1
)?sk2
:sk1
;
915 void MultithreadTest::TestCollators()
918 UErrorCode status
= U_ZERO_ERROR
;
919 FILE *testFile
= NULL
;
920 char testDataPath
[1024];
921 strcpy(testDataPath
, IntlTest::getSourceTestData(status
));
922 if (U_FAILURE(status
)) {
923 errln("ERROR: could not open test data %s", u_errorName(status
));
926 strcat(testDataPath
, "CollationTest_");
928 const char* type
= "NON_IGNORABLE";
930 const char *ext
= ".txt";
935 strcpy(buffer
, testDataPath
);
936 strcat(buffer
, type
);
937 size_t bufLen
= strlen(buffer
);
939 // we try to open 3 files:
940 // path/CollationTest_type.txt
941 // path/CollationTest_type_SHORT.txt
942 // path/CollationTest_type_STUB.txt
943 // we are going to test with the first one that we manage to open.
945 strcpy(buffer
+bufLen
, ext
);
947 testFile
= fopen(buffer
, "rb");
950 strcpy(buffer
+bufLen
, "_SHORT");
952 testFile
= fopen(buffer
, "rb");
955 strcpy(buffer
+bufLen
, "_STUB");
957 testFile
= fopen(buffer
, "rb");
960 *(buffer
+bufLen
) = 0;
961 dataerrln("could not open any of the conformance test files, tried opening base %s", buffer
);
965 "INFO: Working with the stub file.\n"
966 "If you need the full conformance test, please\n"
967 "download the appropriate data files from:\n"
968 "http://source.icu-project.org/repos/icu/tools/trunk/unicodetools/com/ibm/text/data/");
973 LocalArray
<Line
> lines(new Line
[200000]);
974 memset(lines
.getAlias(), 0, sizeof(Line
)*200000);
980 while (fgets(buffer
, 1024, testFile
) != NULL
) {
981 if(*buffer
== 0 || buffer
[0] == '#') {
982 // Store empty and comment lines so that errors are reported
983 // for the real test file lines.
984 lines
[lineNum
].buflen
= 0;
985 lines
[lineNum
].buff
[0] = 0;
987 int32_t buflen
= u_parseString(buffer
, bufferU
, 1024, &first
, &status
);
988 lines
[lineNum
].buflen
= buflen
;
989 u_memcpy(lines
[lineNum
].buff
, bufferU
, buflen
);
990 lines
[lineNum
].buff
[buflen
] = 0;
995 if(U_FAILURE(status
)) {
996 dataerrln("Couldn't read the test file!");
1000 UVersionInfo uniVersion
;
1001 static const UVersionInfo v62
= { 6, 2, 0, 0 };
1002 u_getUnicodeVersion(uniVersion
);
1003 UBool isAtLeastUCA62
= uprv_memcmp(uniVersion
, v62
, 4) >= 0;
1005 LocalPointer
<Collator
> coll(Collator::createInstance(Locale::getRoot(), status
));
1006 if(U_FAILURE(status
)) {
1007 errcheckln(status
, "Couldn't open UCA collator");
1010 coll
->setAttribute(UCOL_NORMALIZATION_MODE
, UCOL_ON
, status
);
1011 coll
->setAttribute(UCOL_CASE_FIRST
, UCOL_OFF
, status
);
1012 coll
->setAttribute(UCOL_CASE_LEVEL
, UCOL_OFF
, status
);
1013 coll
->setAttribute(UCOL_STRENGTH
, isAtLeastUCA62
? UCOL_IDENTICAL
: UCOL_TERTIARY
, status
);
1014 coll
->setAttribute(UCOL_ALTERNATE_HANDLING
, UCOL_NON_IGNORABLE
, status
);
1016 int32_t spawnResult
= 0;
1017 LocalArray
<CollatorThreadTest
> tests(new CollatorThreadTest
[kCollatorThreadThreads
]);
1019 logln(UnicodeString("Spawning: ") + kCollatorThreadThreads
+ " threads * " + kFormatThreadIterations
+ " iterations each.");
1021 for(j
= 0; j
< kCollatorThreadThreads
; j
++) {
1022 //logln("Setting collator %i", j);
1023 tests
[j
].setCollator(coll
.getAlias(), lines
.getAlias(), lineNum
, isAtLeastUCA62
);
1025 for(j
= 0; j
< kCollatorThreadThreads
; j
++) {
1027 spawnResult
= tests
[j
].start();
1028 if(spawnResult
!= 0) {
1029 errln("%s:%d THREAD INFO: thread %d failed to start with status %d", __FILE__
, __LINE__
, j
, spawnResult
);
1033 logln("Spawned all");
1035 for(int32_t i
=0;i
<kCollatorThreadThreads
;i
++) {
1037 //logln(UnicodeString("Test #") + i + " is complete.. ");
1041 #endif /* #if !UCONFIG_NO_COLLATION */
1046 //-------------------------------------------------------------------------------------------
1048 // StringThreadTest2
1050 //-------------------------------------------------------------------------------------------
1052 const int kStringThreadIterations
= 2500;// # of iterations per thread
1053 const int kStringThreadThreads
= 10; // # of threads to spawn
1056 class StringThreadTest2
: public SimpleThread
1061 static const UnicodeString
*gSharedString
;
1063 StringThreadTest2() // constructor is NOT multithread safe.
1075 for (loopCount
= 0; loopCount
< kStringThreadIterations
; loopCount
++) {
1076 if (*gSharedString
!= "This is the original test string.") {
1077 IntlTest::gTest
->errln("%s:%d Original string is corrupt.", __FILE__
, __LINE__
);
1080 UnicodeString s1
= *gSharedString
;
1082 UnicodeString
s2(s1
);
1083 UnicodeString s3
= *gSharedString
;
1094 const UnicodeString
*StringThreadTest2::gSharedString
= NULL
;
1096 // ** The actual test function.
1099 void MultithreadTest::TestString()
1102 StringThreadTest2::gSharedString
= new UnicodeString("This is the original test string.");
1103 StringThreadTest2 tests
[kStringThreadThreads
];
1105 logln(UnicodeString("Spawning: ") + kStringThreadThreads
+ " threads * " + kStringThreadIterations
+ " iterations each.");
1106 for(j
= 0; j
< kStringThreadThreads
; j
++) {
1107 int32_t threadStatus
= tests
[j
].start();
1108 if (threadStatus
!= 0) {
1109 errln("%s:%d System Error %d starting thread number %d.", __FILE__
, __LINE__
, threadStatus
, j
);
1113 // Force a failure, to verify test is functioning and can report errors.
1114 // const_cast<UnicodeString *>(StringThreadTest2::gSharedString)->setCharAt(5, 'x');
1116 for(j
=0; j
<kStringThreadThreads
; j
++) {
1118 logln(UnicodeString("Test #") + j
+ " is complete.. ");
1121 delete StringThreadTest2::gSharedString
;
1122 StringThreadTest2::gSharedString
= NULL
;
1127 // Test for ticket #10673, race in cache code in AnyTransliterator.
1128 // It's difficult to make the original unsafe code actually fail, but
1129 // this test will fairly reliably take the code path for races in
1130 // populating the cache.
1133 #if !UCONFIG_NO_TRANSLITERATION
1134 Transliterator
*gSharedTranslit
= NULL
;
1135 class TxThread
: public SimpleThread
{
1142 TxThread::~TxThread() {}
1143 void TxThread::run() {
1144 UnicodeString
greekString("\\u03B4\\u03B9\\u03B1\\u03C6\\u03BF\\u03C1\\u03B5\\u03C4\\u03B9\\u03BA\\u03BF\\u03CD\\u03C2");
1145 greekString
= greekString
.unescape();
1146 gSharedTranslit
->transliterate(greekString
);
1147 if (greekString
[0] != 0x64) // 'd'. The whole transliterated string is "diaphoretikous" (accented u).
1149 IntlTest::gTest
->errln("%s:%d Transliteration failed.", __FILE__
, __LINE__
);
1155 void MultithreadTest::TestAnyTranslit() {
1156 #if !UCONFIG_NO_TRANSLITERATION
1157 UErrorCode status
= U_ZERO_ERROR
;
1158 LocalPointer
<Transliterator
> tx(Transliterator::createInstance("Any-Latin", UTRANS_FORWARD
, status
));
1159 if (U_FAILURE(status
)) {
1160 dataerrln("File %s, Line %d: Error, status = %s", __FILE__
, __LINE__
, u_errorName(status
));
1163 gSharedTranslit
= tx
.getAlias();
1164 TxThread threads
[4];
1166 for (i
=0; i
<UPRV_LENGTHOF(threads
); i
++) {
1170 for (i
=0; i
<UPRV_LENGTHOF(threads
); i
++) {
1173 gSharedTranslit
= NULL
;
1174 #endif // !UCONFIG_NO_TRANSLITERATION
1179 // Condition Variables Test
1180 // Create a swarm of threads.
1181 // Using a mutex and a condition variables each thread
1182 // Increments a global count of started threads.
1183 // Broadcasts that it has started.
1184 // Waits on the condition that all threads have started.
1185 // Increments a global count of finished threads.
1186 // Waits on the condition that all threads have finished.
1190 class CondThread
: public SimpleThread
{
1192 CondThread() :fFinished(false) {};
1198 static UMutex gCTMutex
= U_MUTEX_INITIALIZER
;
1199 static UConditionVar gCTConditionVar
= U_CONDITION_INITIALIZER
;
1200 int gConditionTestOne
= 1; // Value one. Non-const, extern linkage to inhibit
1201 // compiler assuming a known value.
1202 int gStartedThreads
;
1203 int gFinishedThreads
;
1204 static const int NUMTHREADS
= 10;
1207 // Worker thread function.
1208 void CondThread::run() {
1209 umtx_lock(&gCTMutex
);
1210 gStartedThreads
+= gConditionTestOne
;
1211 umtx_condBroadcast(&gCTConditionVar
);
1213 while (gStartedThreads
< NUMTHREADS
) {
1214 if (gFinishedThreads
!= 0) {
1215 IntlTest::gTest
->errln("File %s, Line %d: Error, gStartedThreads = %d, gFinishedThreads = %d",
1216 __FILE__
, __LINE__
, gStartedThreads
, gFinishedThreads
);
1218 umtx_condWait(&gCTConditionVar
, &gCTMutex
);
1221 gFinishedThreads
+= gConditionTestOne
;
1223 umtx_condBroadcast(&gCTConditionVar
);
1225 while (gFinishedThreads
< NUMTHREADS
) {
1226 umtx_condWait(&gCTConditionVar
, &gCTMutex
);
1228 umtx_unlock(&gCTMutex
);
1231 void MultithreadTest::TestConditionVariables() {
1232 gStartedThreads
= 0;
1233 gFinishedThreads
= 0;
1236 umtx_lock(&gCTMutex
);
1237 CondThread
*threads
[NUMTHREADS
];
1238 for (i
=0; i
<NUMTHREADS
; ++i
) {
1239 threads
[i
] = new CondThread
;
1240 threads
[i
]->start();
1243 while (gStartedThreads
< NUMTHREADS
) {
1244 umtx_condWait(&gCTConditionVar
, &gCTMutex
);
1247 while (gFinishedThreads
< NUMTHREADS
) {
1248 umtx_condWait(&gCTConditionVar
, &gCTMutex
);
1251 umtx_unlock(&gCTMutex
);
1253 for (i
=0; i
<NUMTHREADS
; ++i
) {
1254 if (!threads
[i
]->fFinished
) {
1255 errln("File %s, Line %d: Error, threads[%d]->fFinished == false", __FILE__
, __LINE__
, i
);
1259 for (i
=0; i
<NUMTHREADS
; ++i
) {
1267 // Unified Cache Test
1270 // Each thread fetches a pair of objects. There are 8 distinct pairs:
1271 // ("en_US", "bs"), ("en_GB", "ca"), ("fr_FR", "ca_AD") etc.
1272 // These pairs represent 8 distinct languages
1274 // Note that only one value per language gets created in the cache.
1275 // In particular each cached value can have multiple keys.
1276 static const char *gCacheLocales
[] = {
1277 "en_US", "en_GB", "fr_FR", "fr",
1278 "de", "sr_ME", "sr_BA", "sr_CS"};
1279 static const char *gCacheLocales2
[] = {
1280 "bs", "ca", "ca_AD", "ca_ES",
1281 "en_US", "fi", "ff_CM", "ff_GN"};
1283 static int32_t gObjectsCreated
= 0; // protected by gCTMutex
1284 static const int32_t CACHE_LOAD
= 3;
1286 class UCTMultiThreadItem
: public SharedObject
{
1289 UCTMultiThreadItem(const char *x
) : value(NULL
) {
1290 value
= uprv_strdup(x
);
1292 virtual ~UCTMultiThreadItem() {
1300 const UCTMultiThreadItem
*LocaleCacheKey
<UCTMultiThreadItem
>::createObject(
1301 const void *context
, UErrorCode
&status
) const {
1302 const UnifiedCache
*cacheContext
= (const UnifiedCache
*) context
;
1304 if (uprv_strcmp(fLoc
.getLanguage(), fLoc
.getName()) != 0) {
1305 const UCTMultiThreadItem
*result
= NULL
;
1306 if (cacheContext
== NULL
) {
1307 UnifiedCache::getByLocale(fLoc
.getLanguage(), result
, status
);
1310 cacheContext
->get(LocaleCacheKey
<UCTMultiThreadItem
>(fLoc
.getLanguage()), result
, status
);
1314 umtx_lock(&gCTMutex
);
1315 bool firstObject
= (gObjectsCreated
== 0);
1317 // Force the first object creation that comes through to wait
1318 // until other have completed. Verifies that cache doesn't
1319 // deadlock when a creation is slow.
1321 // Note that gObjectsCreated needs to be incremeneted from 0 to 1
1322 // early, to keep subsequent threads from entering this path.
1323 gObjectsCreated
= 1;
1324 while (gObjectsCreated
< 3) {
1325 umtx_condWait(&gCTConditionVar
, &gCTMutex
);
1328 umtx_unlock(&gCTMutex
);
1330 const UCTMultiThreadItem
*result
=
1331 new UCTMultiThreadItem(fLoc
.getLanguage());
1332 if (result
== NULL
) {
1333 status
= U_MEMORY_ALLOCATION_ERROR
;
1338 // Log that we created an object. The first object was already counted,
1339 // don't do it again.
1340 umtx_lock(&gCTMutex
);
1342 gObjectsCreated
+= 1;
1344 umtx_condBroadcast(&gCTConditionVar
);
1345 umtx_unlock(&gCTMutex
);
1352 class UnifiedCacheThread
: public SimpleThread
{
1355 const UnifiedCache
*cache
,
1357 const char *loc2
) : fCache(cache
), fLoc(loc
), fLoc2(loc2
) {};
1358 ~UnifiedCacheThread() {};
1360 void exerciseByLocale(const Locale
&);
1361 const UnifiedCache
*fCache
;
1366 void UnifiedCacheThread::exerciseByLocale(const Locale
&locale
) {
1367 UErrorCode status
= U_ZERO_ERROR
;
1368 const UCTMultiThreadItem
*origItem
= NULL
;
1370 LocaleCacheKey
<UCTMultiThreadItem
>(locale
), fCache
, origItem
, status
);
1371 U_ASSERT(U_SUCCESS(status
));
1372 if (uprv_strcmp(locale
.getLanguage(), origItem
->value
)) {
1373 IntlTest::gTest
->errln(
1374 "%s:%d Expected %s, got %s", __FILE__
, __LINE__
,
1375 locale
.getLanguage(),
1379 // Fetch the same item again many times. We should always get the same
1380 // pointer since this client is already holding onto it
1381 for (int32_t i
= 0; i
< 1000; ++i
) {
1382 const UCTMultiThreadItem
*item
= NULL
;
1384 LocaleCacheKey
<UCTMultiThreadItem
>(locale
), fCache
, item
, status
);
1385 if (item
!= origItem
) {
1386 IntlTest::gTest
->errln(
1387 "%s:%d Expected to get the same pointer",
1395 origItem
->removeRef();
1398 void UnifiedCacheThread::run() {
1399 // Run the exercise with 2 different locales so that we can exercise
1400 // eviction more. If each thread exercises just one locale, then
1401 // eviction can't start until the threads end.
1402 exerciseByLocale(fLoc
);
1403 exerciseByLocale(fLoc2
);
1406 void MultithreadTest::TestUnifiedCache() {
1408 // Start with our own local cache so that we have complete control
1409 // and set the eviction policy to evict starting with 2 unused
1411 UErrorCode status
= U_ZERO_ERROR
;
1412 UnifiedCache::getInstance(status
);
1413 UnifiedCache
cache(status
);
1414 cache
.setEvictionPolicy(2, 0, status
);
1415 U_ASSERT(U_SUCCESS(status
));
1417 gFinishedThreads
= 0;
1418 gObjectsCreated
= 0;
1420 UnifiedCacheThread
*threads
[CACHE_LOAD
][UPRV_LENGTHOF(gCacheLocales
)];
1421 for (int32_t i
=0; i
<CACHE_LOAD
; ++i
) {
1422 for (int32_t j
=0; j
<UPRV_LENGTHOF(gCacheLocales
); ++j
) {
1423 // Each thread works with a pair of locales.
1424 threads
[i
][j
] = new UnifiedCacheThread(
1425 &cache
, gCacheLocales
[j
], gCacheLocales2
[j
]);
1426 threads
[i
][j
]->start();
1430 for (int32_t i
=0; i
<CACHE_LOAD
; ++i
) {
1431 for (int32_t j
=0; j
<UPRV_LENGTHOF(gCacheLocales
); ++j
) {
1432 threads
[i
][j
]->join();
1435 // Because of cache eviction, we can't assert exactly how many
1436 // distinct objects get created over the course of this run.
1437 // However we know that at least 8 objects get created because that
1438 // is how many distinct languages we have in our test.
1439 if (gObjectsCreated
< 8) {
1440 errln("%s:%d Too few objects created.", __FILE__
, __LINE__
);
1442 // We know that each thread cannot create more than 2 objects in
1443 // the cache, and there are UPRV_LENGTHOF(gCacheLocales) pairs of
1444 // objects fetched from the cache. If the threads run in series because
1445 // of eviction, at worst case each thread creates two objects.
1446 if (gObjectsCreated
> 2 * CACHE_LOAD
* UPRV_LENGTHOF(gCacheLocales
)) {
1447 errln("%s:%d Too many objects created, got %d, expected %d", __FILE__
, __LINE__
, gObjectsCreated
, 2 * CACHE_LOAD
* UPRV_LENGTHOF(gCacheLocales
));
1451 assertEquals("unused values", 2, cache
.unusedCount());
1454 for (int32_t i
=0; i
<CACHE_LOAD
; ++i
) {
1455 for (int32_t j
=0; j
<UPRV_LENGTHOF(gCacheLocales
); ++j
) {
1456 delete threads
[i
][j
];
1461 #if !UCONFIG_NO_TRANSLITERATION
1463 // BreakTransliterator Threading Test
1464 // This is a test for bug #11603. Test verified to fail prior to fix.
1467 static const Transliterator
*gSharedTransliterator
;
1468 static const UnicodeString
*gTranslitInput
;
1469 static const UnicodeString
*gTranslitExpected
;
1471 class BreakTranslitThread
: public SimpleThread
{
1473 BreakTranslitThread() {};
1474 ~BreakTranslitThread() {};
1478 void BreakTranslitThread::run() {
1479 for (int i
=0; i
<10; i
++) {
1480 icu::UnicodeString
s(*gTranslitInput
);
1481 gSharedTransliterator
->transliterate(s
);
1482 if (*gTranslitExpected
!= s
) {
1483 IntlTest::gTest
->errln("%s:%d Transliteration threading failure.", __FILE__
, __LINE__
);
1489 void MultithreadTest::TestBreakTranslit() {
1490 UErrorCode status
= U_ZERO_ERROR
;
1491 UnicodeString
input(
1492 "\\u0E42\\u0E14\\u0E22\\u0E1E\\u0E37\\u0E49\\u0E19\\u0E10\\u0E32\\u0E19\\u0E41\\u0E25\\u0E49\\u0E27,");
1493 input
= input
.unescape();
1494 gTranslitInput
= &input
;
1496 gSharedTransliterator
= Transliterator::createInstance(
1497 UNICODE_STRING_SIMPLE("Any-Latin; Lower; NFD; [:Diacritic:]Remove; NFC; Latin-ASCII;"), UTRANS_FORWARD
, status
);
1498 if (!gSharedTransliterator
) {
1501 TSMTHREAD_ASSERT_SUCCESS(status
);
1503 UnicodeString
expected(*gTranslitInput
);
1504 gSharedTransliterator
->transliterate(expected
);
1505 gTranslitExpected
= &expected
;
1507 BreakTranslitThread threads
[4];
1508 for (int i
=0; i
<UPRV_LENGTHOF(threads
); ++i
) {
1511 for (int i
=0; i
<UPRV_LENGTHOF(threads
); ++i
) {
1515 delete gSharedTransliterator
;
1516 gTranslitInput
= NULL
;
1517 gTranslitExpected
= NULL
;
1521 class TestIncDecThread
: public SimpleThread
{
1523 TestIncDecThread() { };
1527 static u_atomic_int32_t gIncDecCounter
;
1529 void TestIncDecThread::run() {
1530 umtx_atomic_inc(&gIncDecCounter
);
1531 for (int32_t i
=0; i
<5000000; ++i
) {
1532 umtx_atomic_inc(&gIncDecCounter
);
1533 umtx_atomic_dec(&gIncDecCounter
);
1537 void MultithreadTest::TestIncDec()
1539 static constexpr int NUM_THREADS
= 4;
1541 TestIncDecThread threads
[NUM_THREADS
];
1542 for (auto &thread
:threads
) {
1545 for (auto &thread
:threads
) {
1548 assertEquals("TestIncDec", NUM_THREADS
, gIncDecCounter
);
1552 #endif /* !UCONFIG_NO_TRANSLITERATION */