1 // Copyright (C) 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
49 #include "unicode/putil.h"
52 #include "unicode/numfmt.h"
53 #include "unicode/choicfmt.h"
54 #include "unicode/msgfmt.h"
55 #include "unicode/locid.h"
56 #include "unicode/coll.h"
57 #include "unicode/calendar.h"
61 void MultithreadTest::runIndexedTest( int32_t index, UBool exec,
62 const char* &name, char* /*par*/ ) {
64 logln("TestSuite MultithreadTest: ");
79 name = "TestThreadedIntl";
80 #if !UCONFIG_NO_FORMATTING
88 name = "TestCollators";
89 #if !UCONFIG_NO_COLLATION
93 #endif /* #if !UCONFIG_NO_COLLATION */
104 name = "TestArabicShapingThreads";
106 TestArabicShapingThreads();
111 name = "TestAnyTranslit";
118 name = "TestConditionVariables";
120 TestConditionVariables();
124 name = "TestUnifiedCache";
129 #if !UCONFIG_NO_TRANSLITERATION
131 name = "TestBreakTranslit";
139 break; //needed to end loop
144 //-----------------------------------------------------------------------------------
146 // TestThreads -- see if threads really work at all.
148 // Set up N threads pointing at N chars. When they are started, they will
149 // set their chars. At the end we make sure they are all set.
151 //-----------------------------------------------------------------------------------
153 class TestThreadsThread : public SimpleThread
156 TestThreadsThread(char* whatToChange) { fWhatToChange = whatToChange; }
157 virtual void run() { Mutex m;
158 *fWhatToChange = '*';
165 void MultithreadTest::TestThreads()
167 static const int32_t THREADTEST_NRTHREADS = 8;
168 char threadTestChars[THREADTEST_NRTHREADS + 1];
169 SimpleThread *threads[THREADTEST_NRTHREADS];
170 int32_t numThreadsStarted = 0;
173 for(i=0;i<THREADTEST_NRTHREADS;i++)
175 threadTestChars[i] = ' ';
176 threads[i] = new TestThreadsThread(&threadTestChars[i]);
178 threadTestChars[THREADTEST_NRTHREADS] = '\0';
180 logln("->" + UnicodeString(threadTestChars) + "<- Firing off threads.. ");
181 for(i=0;i<THREADTEST_NRTHREADS;i++)
183 if (threads[i]->start() != 0) {
184 errln("Error starting thread %d", i);
189 logln(" Subthread started.");
192 if (numThreadsStarted != THREADTEST_NRTHREADS) {
193 errln("Not all threads could be started for testing!");
197 logln("Waiting for threads to be set..");
198 for(i=0; i<THREADTEST_NRTHREADS; i++) {
200 if (threadTestChars[i] != '*') {
201 errln("%s:%d Thread %d failed.", __FILE__, __LINE__, i);
208 //-----------------------------------------------------------------------------------
210 // TestArabicShapeThreads -- see if calls to u_shapeArabic in many threads works successfully
212 // Set up N threads pointing at N chars. When they are started, they will make calls to doTailTest which tests
213 // u_shapeArabic, if the calls are successful it will the set * chars.
214 // At the end we make sure all threads managed to run u_shapeArabic successfully.
215 // This is a unit test for ticket 9473
217 //-----------------------------------------------------------------------------------
219 class TestArabicShapeThreads : public SimpleThread
222 TestArabicShapeThreads() {};
223 virtual void run() { doTailTest(); };
229 void TestArabicShapeThreads::doTailTest(void) {
230 static const UChar src[] = { 0x0020, 0x0633, 0 };
231 static const UChar dst_old[] = { 0xFEB1, 0x200B,0 };
232 static const UChar dst_new[] = { 0xFEB1, 0xFE73,0 };
233 UChar dst[3] = { 0x0000, 0x0000,0 };
237 for (int32_t loopCount = 0; loopCount < 100; loopCount++) {
238 status = U_ZERO_ERROR;
239 length = u_shapeArabic(src, -1, dst, UPRV_LENGTHOF(dst),
240 U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR, &status);
241 if(U_FAILURE(status)) {
242 IntlTest::gTest->errln("Fail: status %s\n", u_errorName(status));
244 } else if(length!=2) {
245 IntlTest::gTest->errln("Fail: len %d expected 3\n", length);
247 } else if(u_strncmp(dst,dst_old,UPRV_LENGTHOF(dst))) {
248 IntlTest::gTest->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
249 dst[0],dst[1],dst_old[0],dst_old[1]);
255 status = U_ZERO_ERROR;
256 length = u_shapeArabic(src, -1, dst, UPRV_LENGTHOF(dst),
257 U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR|U_SHAPE_TAIL_NEW_UNICODE, &status);
258 if(U_FAILURE(status)) {
259 IntlTest::gTest->errln("Fail: status %s\n", u_errorName(status));
261 } else if(length!=2) {
262 IntlTest::gTest->errln("Fail: len %d expected 3\n", length);
264 } else if(u_strncmp(dst,dst_new,UPRV_LENGTHOF(dst))) {
265 IntlTest::gTest->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
266 dst[0],dst[1],dst_new[0],dst_new[1]);
274 void MultithreadTest::TestArabicShapingThreads()
276 TestArabicShapeThreads threads[30];
280 logln("-> do TestArabicShapingThreads <- Firing off threads.. ");
281 for(i=0; i < UPRV_LENGTHOF(threads); i++) {
282 if (threads[i].start() != 0) {
283 errln("Error starting thread %d", i);
287 for(i=0; i < UPRV_LENGTHOF(threads); i++) {
290 logln("->TestArabicShapingThreads <- Got all threads! cya");
294 //-----------------------------------------------------------------------
296 // TestMutex - a simple (non-stress) test to verify that ICU mutexes
297 // and condition variables are functioning. Does not test the use of
298 // mutexes within ICU services, but rather that the
299 // platform's mutex support is at least superficially there.
301 //----------------------------------------------------------------------
302 static UMutex gTestMutexA = U_MUTEX_INITIALIZER;
303 static UConditionVar gThreadsCountChanged = U_CONDITION_INITIALIZER;
305 static int gThreadsStarted = 0;
306 static int gThreadsInMiddle = 0;
307 static int gThreadsDone = 0;
309 static const int TESTMUTEX_THREAD_COUNT = 40;
311 class TestMutexThread : public SimpleThread
315 // This is the code that each of the spawned threads runs.
316 // All threads move together throught the started - middle - done sequence together,
317 // waiting for all other threads to reach each point before advancing.
318 umtx_lock(&gTestMutexA);
319 gThreadsStarted += 1;
320 umtx_condBroadcast(&gThreadsCountChanged);
321 while (gThreadsStarted < TESTMUTEX_THREAD_COUNT) {
322 if (gThreadsInMiddle != 0) {
323 IntlTest::gTest->errln(
324 "%s:%d gThreadsInMiddle = %d. Expected 0.", __FILE__, __LINE__, gThreadsInMiddle);
327 umtx_condWait(&gThreadsCountChanged, &gTestMutexA);
330 gThreadsInMiddle += 1;
331 umtx_condBroadcast(&gThreadsCountChanged);
332 while (gThreadsInMiddle < TESTMUTEX_THREAD_COUNT) {
333 if (gThreadsDone != 0) {
334 IntlTest::gTest->errln(
335 "%s:%d gThreadsDone = %d. Expected 0.", __FILE__, __LINE__, gThreadsDone);
338 umtx_condWait(&gThreadsCountChanged, &gTestMutexA);
342 umtx_condBroadcast(&gThreadsCountChanged);
343 while (gThreadsDone < TESTMUTEX_THREAD_COUNT) {
344 umtx_condWait(&gThreadsCountChanged, &gTestMutexA);
346 umtx_unlock(&gTestMutexA);
350 void MultithreadTest::TestMutex()
353 gThreadsInMiddle = 0;
356 TestMutexThread threads[TESTMUTEX_THREAD_COUNT];
357 umtx_lock(&gTestMutexA);
358 for (i=0; i<TESTMUTEX_THREAD_COUNT; i++) {
359 if (threads[i].start() != 0) {
360 errln("%s:%d Error starting thread %d", __FILE__, __LINE__, i);
365 // Because we are holding gTestMutexA, all of the threads should be blocked
366 // at the start of their run() function.
367 if (gThreadsStarted != 0) {
368 errln("%s:%d gThreadsStarted=%d. Expected 0.", __FILE__, __LINE__, gThreadsStarted);
372 while (gThreadsInMiddle < TESTMUTEX_THREAD_COUNT) {
373 if (gThreadsDone != 0) {
374 errln("%s:%d gThreadsDone=%d. Expected 0.", __FILE__, __LINE__, gThreadsStarted);
377 umtx_condWait(&gThreadsCountChanged, &gTestMutexA);
380 while (gThreadsDone < TESTMUTEX_THREAD_COUNT) {
381 umtx_condWait(&gThreadsCountChanged, &gTestMutexA);
383 umtx_unlock(&gTestMutexA);
385 for (i=0; i<TESTMUTEX_THREAD_COUNT; i++) {
391 //-------------------------------------------------------------------------------------------
393 // TestMultithreadedIntl. Test ICU Formatting in a multi-threaded environment
395 //-------------------------------------------------------------------------------------------
398 // * Show exactly where the string's differences lie.
399 UnicodeString showDifference(const UnicodeString& expected, const UnicodeString& result)
402 res = expected + "<Expected\n";
403 if(expected.length() != result.length())
404 res += " [ Different lengths ] \n";
407 for(int32_t i=0;i<expected.length();i++)
409 if(expected[i] == result[i])
418 res += "<Differences";
421 res += result + "<Result\n";
427 //-------------------------------------------------------------------------------------------
429 // FormatThreadTest - a thread that tests performing a number of numberformats.
431 //-------------------------------------------------------------------------------------------
433 const int kFormatThreadIterations = 100; // # of iterations per thread
434 const int kFormatThreadThreads = 10; // # of threads to spawn
436 #if !UCONFIG_NO_FORMATTING
440 struct FormatThreadTestData
443 UnicodeString string;
444 FormatThreadTestData(double a, const UnicodeString& b) : number(a),string(b) {}
448 // "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3 number,currency}."
450 static void formatErrorMessage(UErrorCode &realStatus, const UnicodeString& pattern, const Locale& theLocale,
451 UErrorCode inStatus0, /* statusString 1 */ const Locale &inCountry2, double currency3, // these numbers are the message arguments.
452 UnicodeString &result)
454 if(U_FAILURE(realStatus))
455 return; // you messed up
457 UnicodeString errString1(u_errorName(inStatus0));
459 UnicodeString countryName2;
460 inCountry2.getDisplayCountry(theLocale,countryName2);
462 Formattable myArgs[] = {
463 Formattable((int32_t)inStatus0), // inStatus0 {0}
464 Formattable(errString1), // statusString1 {1}
465 Formattable(countryName2), // inCountry2 {2}
466 Formattable(currency3)// currency3 {3,number,currency}
469 MessageFormat *fmt = new MessageFormat("MessageFormat's API is broken!!!!!!!!!!!",realStatus);
470 fmt->setLocale(theLocale);
471 fmt->applyPattern(pattern, realStatus);
473 if (U_FAILURE(realStatus)) {
478 FieldPosition ignore = 0;
479 fmt->format(myArgs,4,result,ignore,realStatus);
485 * Shared formatters & data used by instances of ThreadSafeFormat.
486 * Exactly one instance of this class is created, and it is then shared concurrently
487 * by the multiple instances of ThreadSafeFormat.
489 class ThreadSafeFormatSharedData {
491 ThreadSafeFormatSharedData(UErrorCode &status);
492 ~ThreadSafeFormatSharedData();
493 LocalPointer<NumberFormat> fFormat;
494 Formattable fYDDThing;
495 Formattable fBBDThing;
496 UnicodeString fYDDStr;
497 UnicodeString fBBDStr;
500 const ThreadSafeFormatSharedData *gSharedData = NULL;
502 ThreadSafeFormatSharedData::ThreadSafeFormatSharedData(UErrorCode &status) {
503 fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status));
504 static const UChar kYDD[] = { 0x59, 0x44, 0x44, 0x00 };
505 static const UChar kBBD[] = { 0x42, 0x42, 0x44, 0x00 };
506 fYDDThing.adoptObject(new CurrencyAmount(123.456, kYDD, status));
507 fBBDThing.adoptObject(new CurrencyAmount(987.654, kBBD, status));
508 if (U_FAILURE(status)) {
511 fFormat->format(fYDDThing, fYDDStr, NULL, status);
512 fFormat->format(fBBDThing, fBBDStr, NULL, status);
516 ThreadSafeFormatSharedData::~ThreadSafeFormatSharedData() {
521 * Class for thread-safe testing of format.
522 * Instances of this class appear as members of class FormatThreadTest.
523 * Multiple instances of FormatThreadTest coexist.
524 * ThreadSafeFormat::doStuff() is called concurrently to test the thread safety of
525 * various shared format operations.
527 class ThreadSafeFormat {
529 /* give a unique offset to each thread */
530 ThreadSafeFormat(UErrorCode &status);
531 UBool doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const;
533 LocalPointer<NumberFormat> fFormat; // formatter - en_US constructed currency
537 ThreadSafeFormat::ThreadSafeFormat(UErrorCode &status) {
538 fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status));
541 static const UChar kUSD[] = { 0x55, 0x53, 0x44, 0x00 };
543 UBool ThreadSafeFormat::doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const {
546 if(u_strcmp(fFormat->getCurrency(), kUSD)) {
547 appendErr.append("fFormat currency != ")
550 .append(fFormat->getCurrency())
555 if(u_strcmp(gSharedData->fFormat->getCurrency(), kUSD)) {
556 appendErr.append("gFormat currency != ")
559 .append(gSharedData->fFormat->getCurrency())
564 const UnicodeString *o=NULL;
566 const NumberFormat *nf = NULL; // only operate on it as const.
568 case 0: f = gSharedData->fYDDThing; o = &gSharedData->fYDDStr; nf = gSharedData->fFormat.getAlias(); break;
569 case 1: f = gSharedData->fBBDThing; o = &gSharedData->fBBDStr; nf = gSharedData->fFormat.getAlias(); break;
570 case 2: f = gSharedData->fYDDThing; o = &gSharedData->fYDDStr; nf = fFormat.getAlias(); break;
571 case 3: f = gSharedData->fBBDThing; o = &gSharedData->fBBDStr; nf = fFormat.getAlias(); break;
573 nf->format(f, str, NULL, status);
576 appendErr.append(showDifference(*o, str));
582 UBool U_CALLCONV isAcceptable(void *, const char *, const char *, const UDataInfo *) {
586 //static UMTX debugMutex = NULL;
587 //static UMTX gDebugMutex;
590 class FormatThreadTest : public SimpleThread
596 LocalPointer<ThreadSafeFormat> fTSF;
598 FormatThreadTest() // constructor is NOT multithread safe.
606 UErrorCode status = U_ZERO_ERROR; // TODO: rearrange code to allow checking of status.
607 fTSF.adoptInstead(new ThreadSafeFormat(status));
608 static int32_t fgOffset = 0;
617 LocalPointer<NumberFormat> percentFormatter;
618 UErrorCode status = U_ZERO_ERROR;
622 for (int i=0; i<4000; i++) {
623 status = U_ZERO_ERROR;
624 UDataMemory *data1 = udata_openChoice(0, "res", "en_US", isAcceptable, 0, &status);
625 UDataMemory *data2 = udata_openChoice(0, "res", "fr", isAcceptable, 0, &status);
628 if (U_FAILURE(status)) {
629 error("udata_openChoice failed.\n");
639 for (m=0; m<4000; m++) {
640 status = U_ZERO_ERROR;
641 UResourceBundle *res = NULL;
642 const char *localeName = NULL;
644 Locale loc = Locale::getEnglish();
646 localeName = loc.getName();
647 // localeName = "en";
649 // ResourceBundle bund = ResourceBundle(0, loc, status);
650 //umtx_lock(&gDebugMutex);
651 res = ures_open(NULL, localeName, &status);
652 //umtx_unlock(&gDebugMutex);
654 //umtx_lock(&gDebugMutex);
656 //umtx_unlock(&gDebugMutex);
658 if (U_FAILURE(status)) {
659 error("Resource bundle construction failed.\n");
666 // Keep this data here to avoid static initialization.
667 FormatThreadTestData kNumberFormatTestData[] =
669 FormatThreadTestData((double)5.0, UnicodeString("5", "")),
670 FormatThreadTestData( 6.0, UnicodeString("6", "")),
671 FormatThreadTestData( 20.0, UnicodeString("20", "")),
672 FormatThreadTestData( 8.0, UnicodeString("8", "")),
673 FormatThreadTestData( 8.3, UnicodeString("8.3", "")),
674 FormatThreadTestData( 12345, UnicodeString("12,345", "")),
675 FormatThreadTestData( 81890.23, UnicodeString("81,890.23", "")),
677 int32_t kNumberFormatTestDataLength = UPRV_LENGTHOF(kNumberFormatTestData);
679 // Keep this data here to avoid static initialization.
680 FormatThreadTestData kPercentFormatTestData[] =
682 FormatThreadTestData((double)5.0, CharsToUnicodeString("500\\u00a0%")),
683 FormatThreadTestData( 1.0, CharsToUnicodeString("100\\u00a0%")),
684 FormatThreadTestData( 0.26, CharsToUnicodeString("26\\u00a0%")),
685 FormatThreadTestData(
686 16384.99, CharsToUnicodeString("1\\u00a0638\\u00a0499\\u00a0%")), // U+00a0 = NBSP
687 FormatThreadTestData(
688 81890.23, CharsToUnicodeString("8\\u00a0189\\u00a0023\\u00a0%")),
690 int32_t kPercentFormatTestDataLength = UPRV_LENGTHOF(kPercentFormatTestData);
693 status = U_ZERO_ERROR;
694 LocalPointer<NumberFormat> formatter(NumberFormat::createInstance(Locale::getEnglish(),status));
695 if(U_FAILURE(status)) {
696 IntlTest::gTest->dataerrln("%s:%d Error %s on NumberFormat::createInstance().",
697 __FILE__, __LINE__, u_errorName(status));
698 goto cleanupAndReturn;
701 percentFormatter.adoptInstead(NumberFormat::createPercentInstance(Locale::getFrench(),status));
702 if(U_FAILURE(status)) {
703 IntlTest::gTest->errln("%s:%d Error %s on NumberFormat::createPercentInstance().",
704 __FILE__, __LINE__, u_errorName(status));
705 goto cleanupAndReturn;
708 for(iteration = 0;!IntlTest::gTest->getErrors() && iteration<kFormatThreadIterations;iteration++)
711 int32_t whichLine = (iteration + fOffset)%kNumberFormatTestDataLength;
713 UnicodeString output;
715 formatter->format(kNumberFormatTestData[whichLine].number, output);
717 if(0 != output.compare(kNumberFormatTestData[whichLine].string)) {
718 IntlTest::gTest->errln("format().. expected " + kNumberFormatTestData[whichLine].string
720 goto cleanupAndReturn;
723 // Now check percent.
725 whichLine = (iteration + fOffset)%kPercentFormatTestDataLength;
727 percentFormatter->format(kPercentFormatTestData[whichLine].number, output);
728 if(0 != output.compare(kPercentFormatTestData[whichLine].string))
730 IntlTest::gTest->errln("percent format().. \n" +
731 showDifference(kPercentFormatTestData[whichLine].string,output));
732 goto cleanupAndReturn;
735 // Test message error
736 const int kNumberOfMessageTests = 3;
737 UErrorCode statusToCheck;
738 UnicodeString patternToCheck;
739 Locale messageLocale;
740 Locale countryToCheck;
741 double currencyToCheck;
743 UnicodeString expected;
746 switch((iteration+fOffset) % kNumberOfMessageTests)
750 statusToCheck= U_FILE_ACCESS_ERROR;
751 patternToCheck= "0:Someone from {2} is receiving a #{0}"
752 " error - {1}. Their telephone call is costing "
753 "{3,number,currency}."; // number,currency
754 messageLocale= Locale("en","US");
755 countryToCheck= Locale("","HR");
756 currencyToCheck= 8192.77;
757 expected= "0:Someone from Croatia is receiving a #4 error - "
758 "U_FILE_ACCESS_ERROR. Their telephone call is costing $8,192.77.";
761 statusToCheck= U_INDEX_OUTOFBOUNDS_ERROR;
762 patternToCheck= "1:A customer in {2} is receiving a #{0} error - {1}. "
763 "Their telephone call is costing {3,number,currency}."; // number,currency
764 messageLocale= Locale("de","DE@currency=DEM");
765 countryToCheck= Locale("","BF");
766 currencyToCheck= 2.32;
767 expected= CharsToUnicodeString(
768 "1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. "
769 "Their telephone call is costing 2,32\\u00A0DM.");
772 statusToCheck= U_MEMORY_ALLOCATION_ERROR;
773 patternToCheck= "2:user in {2} is receiving a #{0} error - {1}. "
774 "They insist they just spent {3,number,currency} "
775 "on memory."; // number,currency
776 messageLocale= Locale("de","AT@currency=ATS"); // Austrian German
777 countryToCheck= Locale("","US"); // hmm
778 currencyToCheck= 40193.12;
779 expected= CharsToUnicodeString(
780 "2:user in Vereinigte Staaten is receiving a #7 error"
781 " - U_MEMORY_ALLOCATION_ERROR. They insist they just spent"
782 " \\u00f6S\\u00A040.193,12 on memory.");
786 UnicodeString result;
787 UErrorCode status = U_ZERO_ERROR;
788 formatErrorMessage(status,patternToCheck,messageLocale,statusToCheck,
789 countryToCheck,currencyToCheck,result);
790 if(U_FAILURE(status))
792 UnicodeString tmp(u_errorName(status));
793 IntlTest::gTest->errln("Failure on message format, pattern=" + patternToCheck +
795 goto cleanupAndReturn;
798 if(result != expected)
800 IntlTest::gTest->errln("PatternFormat: \n" + showDifference(expected,result));
801 goto cleanupAndReturn;
803 // test the Thread Safe Format
804 UnicodeString appendErr;
805 if(!fTSF->doStuff(fNum, appendErr, status)) {
806 IntlTest::gTest->errln(appendErr);
807 goto cleanupAndReturn;
809 } /* end of for loop */
818 int32_t fOffset; // where we are testing from.
821 // ** The actual test function.
823 void MultithreadTest::TestThreadedIntl()
825 UnicodeString theErr;
827 UErrorCode threadSafeErr = U_ZERO_ERROR;
829 ThreadSafeFormatSharedData sharedData(threadSafeErr);
830 assertSuccess("initializing ThreadSafeFormat", threadSafeErr, TRUE);
833 // Create and start the test threads
835 logln("Spawning: %d threads * %d iterations each.",
836 kFormatThreadThreads, kFormatThreadIterations);
837 FormatThreadTest tests[kFormatThreadThreads];
839 for(j = 0; j < UPRV_LENGTHOF(tests); j++) {
841 int32_t threadStatus = tests[j].start();
842 if (threadStatus != 0) {
843 errln("%s:%d System Error %d starting thread number %d.",
844 __FILE__, __LINE__, threadStatus, j);
850 for (j=0; j<UPRV_LENGTHOF(tests); j++) {
852 logln("Thread # %d is complete..", j);
855 #endif /* #if !UCONFIG_NO_FORMATTING */
861 //-------------------------------------------------------------------------------------------
863 // Collation threading test
865 //-------------------------------------------------------------------------------------------
866 #if !UCONFIG_NO_COLLATION
868 #define kCollatorThreadThreads 10 // # of threads to spawn
869 #define kCollatorThreadPatience kCollatorThreadThreads*30
877 skipLineBecauseOfBug(const UChar *s, int32_t length) {
878 // TODO: Fix ICU ticket #8052
880 (s[0] == 0xfb2 || s[0] == 0xfb3) &&
882 (s[2] == 0xf73 || s[2] == 0xf75 || s[2] == 0xf81)) {
888 static UCollationResult
889 normalizeResult(int32_t result) {
890 return result<0 ? UCOL_LESS : result==0 ? UCOL_EQUAL : UCOL_GREATER;
893 class CollatorThreadTest : public SimpleThread
896 const Collator *coll;
899 UBool isAtLeastUCA62;
901 CollatorThreadTest() : SimpleThread(),
908 void setCollator(Collator *c, Line *l, int32_t nl, UBool atLeastUCA62)
913 isAtLeastUCA62 = atLeastUCA62;
916 uint8_t sk1[1024], sk2[1024];
917 uint8_t *oldSk = NULL, *newSk = sk1;
922 for(i = 0; i < noLines; i++) {
923 if(lines[i].buflen == 0) { continue; }
925 if(skipLineBecauseOfBug(lines[i].buff, lines[i].buflen)) { continue; }
927 int32_t resLen = coll->getSortKey(lines[i].buff, lines[i].buflen, newSk, 1024);
930 int32_t skres = strcmp((char *)oldSk, (char *)newSk);
931 int32_t cmpres = coll->compare(lines[prev].buff, lines[prev].buflen, lines[i].buff, lines[i].buflen);
932 int32_t cmpres2 = coll->compare(lines[i].buff, lines[i].buflen, lines[prev].buff, lines[prev].buflen);
934 if(cmpres != -cmpres2) {
935 IntlTest::gTest->errln(UnicodeString("Compare result not symmetrical on line ") + (i + 1));
939 if(cmpres != normalizeResult(skres)) {
940 IntlTest::gTest->errln(UnicodeString("Difference between coll->compare and sortkey compare on line ") + (i + 1));
944 int32_t res = cmpres;
945 if(res == 0 && !isAtLeastUCA62) {
946 // Up to UCA 6.1, the collation test files use a custom tie-breaker,
947 // comparing the raw input strings.
948 res = u_strcmpCodePointOrder(lines[prev].buff, lines[i].buff);
949 // Starting with UCA 6.2, the collation test files use the standard UCA tie-breaker,
950 // comparing the NFD versions of the input strings,
951 // which we do via setting strength=identical.
954 IntlTest::gTest->errln(UnicodeString("Line is not greater or equal than previous line, for line ") + (i + 1));
961 (void)oldLen; // Suppress set but not used warning.
964 newSk = (newSk == sk1)?sk2:sk1;
969 void MultithreadTest::TestCollators()
972 UErrorCode status = U_ZERO_ERROR;
973 FILE *testFile = NULL;
974 char testDataPath[1024];
975 strcpy(testDataPath, IntlTest::getSourceTestData(status));
976 if (U_FAILURE(status)) {
977 errln("ERROR: could not open test data %s", u_errorName(status));
980 strcat(testDataPath, "CollationTest_");
982 const char* type = "NON_IGNORABLE";
984 const char *ext = ".txt";
989 strcpy(buffer, testDataPath);
990 strcat(buffer, type);
991 size_t bufLen = strlen(buffer);
993 // we try to open 3 files:
994 // path/CollationTest_type.txt
995 // path/CollationTest_type_SHORT.txt
996 // path/CollationTest_type_STUB.txt
997 // we are going to test with the first one that we manage to open.
999 strcpy(buffer+bufLen, ext);
1001 testFile = fopen(buffer, "rb");
1004 strcpy(buffer+bufLen, "_SHORT");
1005 strcat(buffer, ext);
1006 testFile = fopen(buffer, "rb");
1009 strcpy(buffer+bufLen, "_STUB");
1010 strcat(buffer, ext);
1011 testFile = fopen(buffer, "rb");
1013 if (testFile == 0) {
1014 *(buffer+bufLen) = 0;
1015 dataerrln("could not open any of the conformance test files, tried opening base %s", buffer);
1019 "INFO: Working with the stub file.\n"
1020 "If you need the full conformance test, please\n"
1021 "download the appropriate data files from:\n"
1022 "http://source.icu-project.org/repos/icu/tools/trunk/unicodetools/com/ibm/text/data/");
1027 LocalArray<Line> lines(new Line[200000]);
1028 memset(lines.getAlias(), 0, sizeof(Line)*200000);
1029 int32_t lineNum = 0;
1031 UChar bufferU[1024];
1034 while (fgets(buffer, 1024, testFile) != NULL) {
1035 if(*buffer == 0 || buffer[0] == '#') {
1036 // Store empty and comment lines so that errors are reported
1037 // for the real test file lines.
1038 lines[lineNum].buflen = 0;
1039 lines[lineNum].buff[0] = 0;
1041 int32_t buflen = u_parseString(buffer, bufferU, 1024, &first, &status);
1042 lines[lineNum].buflen = buflen;
1043 u_memcpy(lines[lineNum].buff, bufferU, buflen);
1044 lines[lineNum].buff[buflen] = 0;
1049 if(U_FAILURE(status)) {
1050 dataerrln("Couldn't read the test file!");
1054 UVersionInfo uniVersion;
1055 static const UVersionInfo v62 = { 6, 2, 0, 0 };
1056 u_getUnicodeVersion(uniVersion);
1057 UBool isAtLeastUCA62 = uprv_memcmp(uniVersion, v62, 4) >= 0;
1059 LocalPointer<Collator> coll(Collator::createInstance(Locale::getRoot(), status));
1060 if(U_FAILURE(status)) {
1061 errcheckln(status, "Couldn't open UCA collator");
1064 coll->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
1065 coll->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status);
1066 coll->setAttribute(UCOL_CASE_LEVEL, UCOL_OFF, status);
1067 coll->setAttribute(UCOL_STRENGTH, isAtLeastUCA62 ? UCOL_IDENTICAL : UCOL_TERTIARY, status);
1068 coll->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE, status);
1070 int32_t spawnResult = 0;
1071 LocalArray<CollatorThreadTest> tests(new CollatorThreadTest[kCollatorThreadThreads]);
1073 logln(UnicodeString("Spawning: ") + kCollatorThreadThreads + " threads * " + kFormatThreadIterations + " iterations each.");
1075 for(j = 0; j < kCollatorThreadThreads; j++) {
1076 //logln("Setting collator %i", j);
1077 tests[j].setCollator(coll.getAlias(), lines.getAlias(), lineNum, isAtLeastUCA62);
1079 for(j = 0; j < kCollatorThreadThreads; j++) {
1081 spawnResult = tests[j].start();
1082 if(spawnResult != 0) {
1083 errln("%s:%d THREAD INFO: thread %d failed to start with status %d", __FILE__, __LINE__, j, spawnResult);
1087 logln("Spawned all");
1089 for(int32_t i=0;i<kCollatorThreadThreads;i++) {
1091 //logln(UnicodeString("Test #") + i + " is complete.. ");
1095 #endif /* #if !UCONFIG_NO_COLLATION */
1100 //-------------------------------------------------------------------------------------------
1102 // StringThreadTest2
1104 //-------------------------------------------------------------------------------------------
1106 const int kStringThreadIterations = 2500;// # of iterations per thread
1107 const int kStringThreadThreads = 10; // # of threads to spawn
1110 class StringThreadTest2 : public SimpleThread
1115 static const UnicodeString *gSharedString;
1117 StringThreadTest2() // constructor is NOT multithread safe.
1129 for (loopCount = 0; loopCount < kStringThreadIterations; loopCount++) {
1130 if (*gSharedString != "This is the original test string.") {
1131 IntlTest::gTest->errln("%s:%d Original string is corrupt.", __FILE__, __LINE__);
1134 UnicodeString s1 = *gSharedString;
1136 UnicodeString s2(s1);
1137 UnicodeString s3 = *gSharedString;
1148 const UnicodeString *StringThreadTest2::gSharedString = NULL;
1150 // ** The actual test function.
1153 void MultithreadTest::TestString()
1156 StringThreadTest2::gSharedString = new UnicodeString("This is the original test string.");
1157 StringThreadTest2 tests[kStringThreadThreads];
1159 logln(UnicodeString("Spawning: ") + kStringThreadThreads + " threads * " + kStringThreadIterations + " iterations each.");
1160 for(j = 0; j < kStringThreadThreads; j++) {
1161 int32_t threadStatus = tests[j].start();
1162 if (threadStatus != 0) {
1163 errln("%s:%d System Error %d starting thread number %d.", __FILE__, __LINE__, threadStatus, j);
1167 // Force a failure, to verify test is functioning and can report errors.
1168 // const_cast<UnicodeString *>(StringThreadTest2::gSharedString)->setCharAt(5, 'x');
1170 for(j=0; j<kStringThreadThreads; j++) {
1172 logln(UnicodeString("Test #") + j + " is complete.. ");
1175 delete StringThreadTest2::gSharedString;
1176 StringThreadTest2::gSharedString = NULL;
1181 // Test for ticket #10673, race in cache code in AnyTransliterator.
1182 // It's difficult to make the original unsafe code actually fail, but
1183 // this test will fairly reliably take the code path for races in
1184 // populating the cache.
1187 #if !UCONFIG_NO_TRANSLITERATION
1188 Transliterator *gSharedTranslit = NULL;
1189 class TxThread: public SimpleThread {
1196 TxThread::~TxThread() {}
1197 void TxThread::run() {
1198 UnicodeString greekString("\\u03B4\\u03B9\\u03B1\\u03C6\\u03BF\\u03C1\\u03B5\\u03C4\\u03B9\\u03BA\\u03BF\\u03CD\\u03C2");
1199 greekString = greekString.unescape();
1200 gSharedTranslit->transliterate(greekString);
1201 if (greekString[0] != 0x64) // 'd'. The whole transliterated string is "diaphoretikous" (accented u).
1203 IntlTest::gTest->errln("%s:%d Transliteration failed.", __FILE__, __LINE__);
1209 void MultithreadTest::TestAnyTranslit() {
1210 #if !UCONFIG_NO_TRANSLITERATION
1211 UErrorCode status = U_ZERO_ERROR;
1212 LocalPointer<Transliterator> tx(Transliterator::createInstance("Any-Latin", UTRANS_FORWARD, status));
1213 if (U_FAILURE(status)) {
1214 dataerrln("File %s, Line %d: Error, status = %s", __FILE__, __LINE__, u_errorName(status));
1217 gSharedTranslit = tx.getAlias();
1218 TxThread threads[4];
1220 for (i=0; i<UPRV_LENGTHOF(threads); i++) {
1224 for (i=0; i<UPRV_LENGTHOF(threads); i++) {
1227 gSharedTranslit = NULL;
1228 #endif // !UCONFIG_NO_TRANSLITERATION
1233 // Condition Variables Test
1234 // Create a swarm of threads.
1235 // Using a mutex and a condition variables each thread
1236 // Increments a global count of started threads.
1237 // Broadcasts that it has started.
1238 // Waits on the condition that all threads have started.
1239 // Increments a global count of finished threads.
1240 // Waits on the condition that all threads have finished.
1244 class CondThread: public SimpleThread {
1246 CondThread() :fFinished(false) {};
1252 static UMutex gCTMutex = U_MUTEX_INITIALIZER;
1253 static UConditionVar gCTConditionVar = U_CONDITION_INITIALIZER;
1254 int gConditionTestOne = 1; // Value one. Non-const, extern linkage to inhibit
1255 // compiler assuming a known value.
1256 int gStartedThreads;
1257 int gFinishedThreads;
1258 static const int NUMTHREADS = 10;
1261 // Worker thread function.
1262 void CondThread::run() {
1263 umtx_lock(&gCTMutex);
1264 gStartedThreads += gConditionTestOne;
1265 umtx_condBroadcast(&gCTConditionVar);
1267 while (gStartedThreads < NUMTHREADS) {
1268 if (gFinishedThreads != 0) {
1269 IntlTest::gTest->errln("File %s, Line %d: Error, gStartedThreads = %d, gFinishedThreads = %d",
1270 __FILE__, __LINE__, gStartedThreads, gFinishedThreads);
1272 umtx_condWait(&gCTConditionVar, &gCTMutex);
1275 gFinishedThreads += gConditionTestOne;
1277 umtx_condBroadcast(&gCTConditionVar);
1279 while (gFinishedThreads < NUMTHREADS) {
1280 umtx_condWait(&gCTConditionVar, &gCTMutex);
1282 umtx_unlock(&gCTMutex);
1285 void MultithreadTest::TestConditionVariables() {
1286 gStartedThreads = 0;
1287 gFinishedThreads = 0;
1290 umtx_lock(&gCTMutex);
1291 CondThread *threads[NUMTHREADS];
1292 for (i=0; i<NUMTHREADS; ++i) {
1293 threads[i] = new CondThread;
1294 threads[i]->start();
1297 while (gStartedThreads < NUMTHREADS) {
1298 umtx_condWait(&gCTConditionVar, &gCTMutex);
1301 while (gFinishedThreads < NUMTHREADS) {
1302 umtx_condWait(&gCTConditionVar, &gCTMutex);
1305 umtx_unlock(&gCTMutex);
1307 for (i=0; i<NUMTHREADS; ++i) {
1308 if (!threads[i]->fFinished) {
1309 errln("File %s, Line %d: Error, threads[%d]->fFinished == false", __FILE__, __LINE__, i);
1313 for (i=0; i<NUMTHREADS; ++i) {
1321 // Unified Cache Test
1324 // Each thread fetches a pair of objects. There are 8 distinct pairs:
1325 // ("en_US", "bs"), ("en_GB", "ca"), ("fr_FR", "ca_AD") etc.
1326 // These pairs represent 8 distinct languages
1328 // Note that only one value per language gets created in the cache.
1329 // In particular each cached value can have multiple keys.
1330 static const char *gCacheLocales[] = {
1331 "en_US", "en_GB", "fr_FR", "fr",
1332 "de", "sr_ME", "sr_BA", "sr_CS"};
1333 static const char *gCacheLocales2[] = {
1334 "bs", "ca", "ca_AD", "ca_ES",
1335 "en_US", "fi", "ff_CM", "ff_GN"};
1337 static int32_t gObjectsCreated = 0; // protected by gCTMutex
1338 static const int32_t CACHE_LOAD = 3;
1340 class UCTMultiThreadItem : public SharedObject {
1343 UCTMultiThreadItem(const char *x) : value(NULL) {
1344 value = uprv_strdup(x);
1346 virtual ~UCTMultiThreadItem() {
1354 const UCTMultiThreadItem *LocaleCacheKey<UCTMultiThreadItem>::createObject(
1355 const void *context, UErrorCode &status) const {
1356 const UnifiedCache *cacheContext = (const UnifiedCache *) context;
1358 if (uprv_strcmp(fLoc.getLanguage(), fLoc.getName()) != 0) {
1359 const UCTMultiThreadItem *result = NULL;
1360 if (cacheContext == NULL) {
1361 UnifiedCache::getByLocale(fLoc.getLanguage(), result, status);
1364 cacheContext->get(LocaleCacheKey<UCTMultiThreadItem>(fLoc.getLanguage()), result, status);
1368 umtx_lock(&gCTMutex);
1369 bool firstObject = (gObjectsCreated == 0);
1371 // Force the first object creation that comes through to wait
1372 // until other have completed. Verifies that cache doesn't
1373 // deadlock when a creation is slow.
1375 // Note that gObjectsCreated needs to be incremeneted from 0 to 1
1376 // early, to keep subsequent threads from entering this path.
1377 gObjectsCreated = 1;
1378 while (gObjectsCreated < 3) {
1379 umtx_condWait(&gCTConditionVar, &gCTMutex);
1382 umtx_unlock(&gCTMutex);
1384 const UCTMultiThreadItem *result =
1385 new UCTMultiThreadItem(fLoc.getLanguage());
1386 if (result == NULL) {
1387 status = U_MEMORY_ALLOCATION_ERROR;
1392 // Log that we created an object. The first object was already counted,
1393 // don't do it again.
1394 umtx_lock(&gCTMutex);
1396 gObjectsCreated += 1;
1398 umtx_condBroadcast(&gCTConditionVar);
1399 umtx_unlock(&gCTMutex);
1406 class UnifiedCacheThread: public SimpleThread {
1409 const UnifiedCache *cache,
1411 const char *loc2) : fCache(cache), fLoc(loc), fLoc2(loc2) {};
1412 ~UnifiedCacheThread() {};
1414 void exerciseByLocale(const Locale &);
1415 const UnifiedCache *fCache;
1420 void UnifiedCacheThread::exerciseByLocale(const Locale &locale) {
1421 UErrorCode status = U_ZERO_ERROR;
1422 const UCTMultiThreadItem *origItem = NULL;
1424 LocaleCacheKey<UCTMultiThreadItem>(locale), fCache, origItem, status);
1425 U_ASSERT(U_SUCCESS(status));
1426 if (uprv_strcmp(locale.getLanguage(), origItem->value)) {
1427 IntlTest::gTest->errln(
1428 "%s:%d Expected %s, got %s", __FILE__, __LINE__,
1429 locale.getLanguage(),
1433 // Fetch the same item again many times. We should always get the same
1434 // pointer since this client is already holding onto it
1435 for (int32_t i = 0; i < 1000; ++i) {
1436 const UCTMultiThreadItem *item = NULL;
1438 LocaleCacheKey<UCTMultiThreadItem>(locale), fCache, item, status);
1439 if (item != origItem) {
1440 IntlTest::gTest->errln(
1441 "%s:%d Expected to get the same pointer",
1449 origItem->removeRef();
1452 void UnifiedCacheThread::run() {
1453 // Run the exercise with 2 different locales so that we can exercise
1454 // eviction more. If each thread exerices just one locale, then
1455 // eviction can't start until the threads end.
1456 exerciseByLocale(fLoc);
1457 exerciseByLocale(fLoc2);
1460 void MultithreadTest::TestUnifiedCache() {
1462 // Start with our own local cache so that we have complete control
1463 // and set the eviction policy to evict starting with 2 unused
1465 UErrorCode status = U_ZERO_ERROR;
1466 UnifiedCache::getInstance(status);
1467 UnifiedCache cache(status);
1468 cache.setEvictionPolicy(2, 0, status);
1469 U_ASSERT(U_SUCCESS(status));
1471 gFinishedThreads = 0;
1472 gObjectsCreated = 0;
1474 UnifiedCacheThread *threads[CACHE_LOAD][UPRV_LENGTHOF(gCacheLocales)];
1475 for (int32_t i=0; i<CACHE_LOAD; ++i) {
1476 for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
1477 // Each thread works with a pair of locales.
1478 threads[i][j] = new UnifiedCacheThread(
1479 &cache, gCacheLocales[j], gCacheLocales2[j]);
1480 threads[i][j]->start();
1484 for (int32_t i=0; i<CACHE_LOAD; ++i) {
1485 for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
1486 threads[i][j]->join();
1489 // Because of cache eviction, we can't assert exactly how many
1490 // distinct objects get created over the course of this run.
1491 // However we know that at least 8 objects get created because that
1492 // is how many distinct languages we have in our test.
1493 if (gObjectsCreated < 8) {
1494 errln("%s:%d Too few objects created.", __FILE__, __LINE__);
1496 // We know that each thread cannot create more than 2 objects in
1497 // the cache, and there are UPRV_LENGTHOF(gCacheLocales) pairs of
1498 // objects fetched from the cache. If the threads run in series because
1499 // of eviction, at worst case each thread creates two objects.
1500 if (gObjectsCreated > 2 * CACHE_LOAD * UPRV_LENGTHOF(gCacheLocales)) {
1501 errln("%s:%d Too many objects created, got %d, expected %d", __FILE__, __LINE__, gObjectsCreated, 2 * CACHE_LOAD * UPRV_LENGTHOF(gCacheLocales));
1505 assertEquals("unused values", 2, cache.unusedCount());
1508 for (int32_t i=0; i<CACHE_LOAD; ++i) {
1509 for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
1510 delete threads[i][j];
1515 #if !UCONFIG_NO_TRANSLITERATION
1517 // BreakTransliterator Threading Test
1518 // This is a test for bug #11603. Test verified to fail prior to fix.
1521 static const Transliterator *gSharedTransliterator;
1522 static const UnicodeString *gTranslitInput;
1523 static const UnicodeString *gTranslitExpected;
1525 class BreakTranslitThread: public SimpleThread {
1527 BreakTranslitThread() {};
1528 ~BreakTranslitThread() {};
1532 void BreakTranslitThread::run() {
1533 for (int i=0; i<10; i++) {
1534 icu::UnicodeString s(*gTranslitInput);
1535 gSharedTransliterator->transliterate(s);
1536 if (*gTranslitExpected != s) {
1537 IntlTest::gTest->errln("%s:%d Transliteration threading failure.", __FILE__, __LINE__);
1543 void MultithreadTest::TestBreakTranslit() {
1544 UErrorCode status = U_ZERO_ERROR;
1545 UnicodeString input(
1546 "\\u0E42\\u0E14\\u0E22\\u0E1E\\u0E37\\u0E49\\u0E19\\u0E10\\u0E32\\u0E19\\u0E41\\u0E25\\u0E49\\u0E27,");
1547 input = input.unescape();
1548 gTranslitInput = &input;
1550 gSharedTransliterator = Transliterator::createInstance(
1551 UNICODE_STRING_SIMPLE("Any-Latin; Lower; NFD; [:Diacritic:]Remove; NFC; Latin-ASCII;"), UTRANS_FORWARD, status);
1552 if (!gSharedTransliterator) {
1555 TSMTHREAD_ASSERT_SUCCESS(status);
1557 UnicodeString expected(*gTranslitInput);
1558 gSharedTransliterator->transliterate(expected);
1559 gTranslitExpected = &expected;
1561 BreakTranslitThread threads[4];
1562 for (int i=0; i<UPRV_LENGTHOF(threads); ++i) {
1565 for (int i=0; i<UPRV_LENGTHOF(threads); ++i) {
1569 delete gSharedTransliterator;
1570 gTranslitInput = NULL;
1571 gTranslitExpected = NULL;
1574 #endif /* !UCONFIG_NO_TRANSLITERATION */