1 // Copyright (C) 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /******************************************************************** 4 * COPYRIGHT: 5 * Copyright (c) 1999-2015, International Business Machines Corporation and 6 * others. All Rights Reserved. 7 ********************************************************************/ 8 9 #include "simplethread.h" 10 11 #include "unicode/utypes.h" 12 #include "unicode/ustring.h" 13 #include "umutex.h" 14 #include "cmemory.h" 15 #include "cstring.h" 16 #include "uparse.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" 22 #include "putilimp.h" 23 #include "intltest.h" 24 #include "tsmthred.h" 25 #include "unicode/ushape.h" 26 #include "unicode/translit.h" 27 #include "sharedobject.h" 28 #include "unifiedcache.h" 29 #include "uassert.h" 30 31 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));}} 36 37 MultithreadTest::MultithreadTest() 38 { 39 } 40 41 MultithreadTest::~MultithreadTest() 42 { 43 } 44 45 #include <stdio.h> 46 #include <string.h> 47 #include <ctype.h> // tolower, toupper 48 49 #include "unicode/putil.h" 50 51 // for mthreadtest 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" 58 #include "ucaconf.h" 59 60 61 void MultithreadTest::runIndexedTest( int32_t index, UBool exec, 62 const char* &name, char* /*par*/ ) { 63 if (exec) 64 logln("TestSuite MultithreadTest: "); 65 switch (index) { 66 case 0: 67 name = "TestThreads"; 68 if (exec) 69 TestThreads(); 70 break; 71 72 case 1: 73 name = "TestMutex"; 74 if (exec) 75 TestMutex(); 76 break; 77 78 case 2: 79 name = "TestThreadedIntl"; 80 #if !UCONFIG_NO_FORMATTING 81 if (exec) { 82 TestThreadedIntl(); 83 } 84 #endif 85 break; 86 87 case 3: 88 name = "TestCollators"; 89 #if !UCONFIG_NO_COLLATION 90 if (exec) { 91 TestCollators(); 92 } 93 #endif /* #if !UCONFIG_NO_COLLATION */ 94 break; 95 96 case 4: 97 name = "TestString"; 98 if (exec) { 99 TestString(); 100 } 101 break; 102 103 case 5: 104 name = "TestArabicShapingThreads"; 105 if (exec) { 106 TestArabicShapingThreads(); 107 } 108 break; 109 110 case 6: 111 name = "TestAnyTranslit"; 112 if (exec) { 113 TestAnyTranslit(); 114 } 115 break; 116 117 case 7: 118 name = "TestConditionVariables"; 119 if (exec) { 120 TestConditionVariables(); 121 } 122 break; 123 case 8: 124 name = "TestUnifiedCache"; 125 if (exec) { 126 TestUnifiedCache(); 127 } 128 break; 129 #if !UCONFIG_NO_TRANSLITERATION 130 case 9: 131 name = "TestBreakTranslit"; 132 if (exec) { 133 TestBreakTranslit(); 134 } 135 break; 136 #endif 137 default: 138 name = ""; 139 break; //needed to end loop 140 } 141 } 142 143 144 //----------------------------------------------------------------------------------- 145 // 146 // TestThreads -- see if threads really work at all. 147 // 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. 150 // 151 //----------------------------------------------------------------------------------- 152 153 class TestThreadsThread : public SimpleThread 154 { 155 public: 156 TestThreadsThread(char* whatToChange) { fWhatToChange = whatToChange; } 157 virtual void run() { Mutex m; 158 *fWhatToChange = '*'; 159 } 160 private: 161 char *fWhatToChange; 162 }; 163 164 165 void MultithreadTest::TestThreads() 166 { 167 static const int32_t THREADTEST_NRTHREADS = 8; 168 char threadTestChars[THREADTEST_NRTHREADS + 1]; 169 SimpleThread *threads[THREADTEST_NRTHREADS]; 170 int32_t numThreadsStarted = 0; 171 172 int32_t i; 173 for(i=0;i<THREADTEST_NRTHREADS;i++) 174 { 175 threadTestChars[i] = ' '; 176 threads[i] = new TestThreadsThread(&threadTestChars[i]); 177 } 178 threadTestChars[THREADTEST_NRTHREADS] = '\0'; 179 180 logln("->" + UnicodeString(threadTestChars) + "<- Firing off threads.. "); 181 for(i=0;i<THREADTEST_NRTHREADS;i++) 182 { 183 if (threads[i]->start() != 0) { 184 errln("Error starting thread %d", i); 185 } 186 else { 187 numThreadsStarted++; 188 } 189 logln(" Subthread started."); 190 } 191 192 if (numThreadsStarted != THREADTEST_NRTHREADS) { 193 errln("Not all threads could be started for testing!"); 194 return; 195 } 196 197 logln("Waiting for threads to be set.."); 198 for(i=0; i<THREADTEST_NRTHREADS; i++) { 199 threads[i]->join(); 200 if (threadTestChars[i] != '*') { 201 errln("%s:%d Thread %d failed.", __FILE__, __LINE__, i); 202 } 203 delete threads[i]; 204 } 205 } 206 207 208 //----------------------------------------------------------------------------------- 209 // 210 // TestArabicShapeThreads -- see if calls to u_shapeArabic in many threads works successfully 211 // 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 216 // 217 //----------------------------------------------------------------------------------- 218 219 class TestArabicShapeThreads : public SimpleThread 220 { 221 public: 222 TestArabicShapeThreads() {}; 223 virtual void run() { doTailTest(); }; 224 private: 225 void doTailTest(); 226 }; 227 228 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 }; 234 int32_t length; 235 UErrorCode status; 236 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)); 243 return; 244 } else if(length!=2) { 245 IntlTest::gTest->errln("Fail: len %d expected 3\n", length); 246 return; 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]); 250 return; 251 } 252 253 254 //"Trying new tail 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)); 260 return; 261 } else if(length!=2) { 262 IntlTest::gTest->errln("Fail: len %d expected 3\n", length); 263 return; 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]); 267 return; 268 } 269 } 270 return; 271 } 272 273 274 void MultithreadTest::TestArabicShapingThreads() 275 { 276 TestArabicShapeThreads threads[30]; 277 278 int32_t i; 279 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); 284 } 285 } 286 287 for(i=0; i < UPRV_LENGTHOF(threads); i++) { 288 threads[i].join(); 289 } 290 logln("->TestArabicShapingThreads <- Got all threads! cya"); 291 } 292 293 294 //----------------------------------------------------------------------- 295 // 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. 300 // 301 //---------------------------------------------------------------------- 302 static UMutex gTestMutexA = U_MUTEX_INITIALIZER; 303 static UConditionVar gThreadsCountChanged = U_CONDITION_INITIALIZER; 304 305 static int gThreadsStarted = 0; 306 static int gThreadsInMiddle = 0; 307 static int gThreadsDone = 0; 308 309 static const int TESTMUTEX_THREAD_COUNT = 40; 310 311 class TestMutexThread : public SimpleThread 312 { 313 public: 314 virtual void run() { 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); 325 return; 326 } 327 umtx_condWait(&gThreadsCountChanged, &gTestMutexA); 328 } 329 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); 336 return; 337 } 338 umtx_condWait(&gThreadsCountChanged, &gTestMutexA); 339 } 340 341 gThreadsDone += 1; 342 umtx_condBroadcast(&gThreadsCountChanged); 343 while (gThreadsDone < TESTMUTEX_THREAD_COUNT) { 344 umtx_condWait(&gThreadsCountChanged, &gTestMutexA); 345 } 346 umtx_unlock(&gTestMutexA); 347 } 348 }; 349 350 void MultithreadTest::TestMutex() 351 { 352 gThreadsStarted = 0; 353 gThreadsInMiddle = 0; 354 gThreadsDone = 0; 355 int32_t i = 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); 361 return; 362 } 363 } 364 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); 369 return; 370 } 371 372 while (gThreadsInMiddle < TESTMUTEX_THREAD_COUNT) { 373 if (gThreadsDone != 0) { 374 errln("%s:%d gThreadsDone=%d. Expected 0.", __FILE__, __LINE__, gThreadsStarted); 375 return; 376 } 377 umtx_condWait(&gThreadsCountChanged, &gTestMutexA); 378 } 379 380 while (gThreadsDone < TESTMUTEX_THREAD_COUNT) { 381 umtx_condWait(&gThreadsCountChanged, &gTestMutexA); 382 } 383 umtx_unlock(&gTestMutexA); 384 385 for (i=0; i<TESTMUTEX_THREAD_COUNT; i++) { 386 threads[i].join(); 387 } 388 } 389 390 391 //------------------------------------------------------------------------------------------- 392 // 393 // TestMultithreadedIntl. Test ICU Formatting in a multi-threaded environment 394 // 395 //------------------------------------------------------------------------------------------- 396 397 398 // * Show exactly where the string's differences lie. 399 UnicodeString showDifference(const UnicodeString& expected, const UnicodeString& result) 400 { 401 UnicodeString res; 402 res = expected + "<Expected\n"; 403 if(expected.length() != result.length()) 404 res += " [ Different lengths ] \n"; 405 else 406 { 407 for(int32_t i=0;i<expected.length();i++) 408 { 409 if(expected[i] == result[i]) 410 { 411 res += " "; 412 } 413 else 414 { 415 res += "|"; 416 } 417 } 418 res += "<Differences"; 419 res += "\n"; 420 } 421 res += result + "<Result\n"; 422 423 return res; 424 } 425 426 427 //------------------------------------------------------------------------------------------- 428 // 429 // FormatThreadTest - a thread that tests performing a number of numberformats. 430 // 431 //------------------------------------------------------------------------------------------- 432 433 const int kFormatThreadIterations = 100; // # of iterations per thread 434 const int kFormatThreadThreads = 10; // # of threads to spawn 435 436 #if !UCONFIG_NO_FORMATTING 437 438 439 440 struct FormatThreadTestData 441 { 442 double number; 443 UnicodeString string; 444 FormatThreadTestData(double a, const UnicodeString& b) : number(a),string(b) {} 445 } ; 446 447 448 // "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3 number,currency}." 449 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) 453 { 454 if(U_FAILURE(realStatus)) 455 return; // you messed up 456 457 UnicodeString errString1(u_errorName(inStatus0)); 458 459 UnicodeString countryName2; 460 inCountry2.getDisplayCountry(theLocale,countryName2); 461 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} 467 }; 468 469 MessageFormat *fmt = new MessageFormat("MessageFormat's API is broken!!!!!!!!!!!",realStatus); 470 fmt->setLocale(theLocale); 471 fmt->applyPattern(pattern, realStatus); 472 473 if (U_FAILURE(realStatus)) { 474 delete fmt; 475 return; 476 } 477 478 FieldPosition ignore = 0; 479 fmt->format(myArgs,4,result,ignore,realStatus); 480 481 delete fmt; 482 } 483 484 /** 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. 488 */ 489 class ThreadSafeFormatSharedData { 490 public: 491 ThreadSafeFormatSharedData(UErrorCode &status); 492 ~ThreadSafeFormatSharedData(); 493 LocalPointer<NumberFormat> fFormat; 494 Formattable fYDDThing; 495 Formattable fBBDThing; 496 UnicodeString fYDDStr; 497 UnicodeString fBBDStr; 498 }; 499 500 const ThreadSafeFormatSharedData *gSharedData = NULL; 501 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)) { 509 return; 510 } 511 fFormat->format(fYDDThing, fYDDStr, NULL, status); 512 fFormat->format(fBBDThing, fBBDStr, NULL, status); 513 gSharedData = this; 514 } 515 516 ThreadSafeFormatSharedData::~ThreadSafeFormatSharedData() { 517 gSharedData = NULL; 518 } 519 520 /** 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. 526 */ 527 class ThreadSafeFormat { 528 public: 529 /* give a unique offset to each thread */ 530 ThreadSafeFormat(UErrorCode &status); 531 UBool doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const; 532 private: 533 LocalPointer<NumberFormat> fFormat; // formatter - en_US constructed currency 534 }; 535 536 537 ThreadSafeFormat::ThreadSafeFormat(UErrorCode &status) { 538 fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status)); 539 } 540 541 static const UChar kUSD[] = { 0x55, 0x53, 0x44, 0x00 }; 542 543 UBool ThreadSafeFormat::doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const { 544 UBool okay = TRUE; 545 546 if(u_strcmp(fFormat->getCurrency(), kUSD)) { 547 appendErr.append("fFormat currency != ") 548 .append(kUSD) 549 .append(", =") 550 .append(fFormat->getCurrency()) 551 .append("! "); 552 okay = FALSE; 553 } 554 555 if(u_strcmp(gSharedData->fFormat->getCurrency(), kUSD)) { 556 appendErr.append("gFormat currency != ") 557 .append(kUSD) 558 .append(", =") 559 .append(gSharedData->fFormat->getCurrency()) 560 .append("! "); 561 okay = FALSE; 562 } 563 UnicodeString str; 564 const UnicodeString *o=NULL; 565 Formattable f; 566 const NumberFormat *nf = NULL; // only operate on it as const. 567 switch(offset%4) { 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; 572 } 573 nf->format(f, str, NULL, status); 574 575 if(*o != str) { 576 appendErr.append(showDifference(*o, str)); 577 okay = FALSE; 578 } 579 return okay; 580 } 581 582 UBool U_CALLCONV isAcceptable(void *, const char *, const char *, const UDataInfo *) { 583 return TRUE; 584 } 585 586 //static UMTX debugMutex = NULL; 587 //static UMTX gDebugMutex; 588 589 590 class FormatThreadTest : public SimpleThread 591 { 592 public: 593 int fNum; 594 int fTraceInfo; 595 596 LocalPointer<ThreadSafeFormat> fTSF; 597 598 FormatThreadTest() // constructor is NOT multithread safe. 599 : SimpleThread(), 600 fNum(0), 601 fTraceInfo(0), 602 fTSF(NULL), 603 fOffset(0) 604 // the locale to use 605 { 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; 609 fgOffset += 3; 610 fOffset = fgOffset; 611 } 612 613 614 virtual void run() 615 { 616 fTraceInfo = 1; 617 LocalPointer<NumberFormat> percentFormatter; 618 UErrorCode status = U_ZERO_ERROR; 619 620 #if 0 621 // debugging code, 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); 626 udata_close(data1); 627 udata_close(data2); 628 if (U_FAILURE(status)) { 629 error("udata_openChoice failed.\n"); 630 break; 631 } 632 } 633 return; 634 #endif 635 636 #if 0 637 // debugging code, 638 int m; 639 for (m=0; m<4000; m++) { 640 status = U_ZERO_ERROR; 641 UResourceBundle *res = NULL; 642 const char *localeName = NULL; 643 644 Locale loc = Locale::getEnglish(); 645 646 localeName = loc.getName(); 647 // localeName = "en"; 648 649 // ResourceBundle bund = ResourceBundle(0, loc, status); 650 //umtx_lock(&gDebugMutex); 651 res = ures_open(NULL, localeName, &status); 652 //umtx_unlock(&gDebugMutex); 653 654 //umtx_lock(&gDebugMutex); 655 ures_close(res); 656 //umtx_unlock(&gDebugMutex); 657 658 if (U_FAILURE(status)) { 659 error("Resource bundle construction failed.\n"); 660 break; 661 } 662 } 663 return; 664 #endif 665 666 // Keep this data here to avoid static initialization. 667 FormatThreadTestData kNumberFormatTestData[] = 668 { 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", "")), 676 }; 677 int32_t kNumberFormatTestDataLength = UPRV_LENGTHOF(kNumberFormatTestData); 678 679 // Keep this data here to avoid static initialization. 680 FormatThreadTestData kPercentFormatTestData[] = 681 { 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%")), 689 }; 690 int32_t kPercentFormatTestDataLength = UPRV_LENGTHOF(kPercentFormatTestData); 691 int32_t iteration; 692 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; 699 } 700 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; 706 } 707 708 for(iteration = 0;!IntlTest::gTest->getErrors() && iteration<kFormatThreadIterations;iteration++) 709 { 710 711 int32_t whichLine = (iteration + fOffset)%kNumberFormatTestDataLength; 712 713 UnicodeString output; 714 715 formatter->format(kNumberFormatTestData[whichLine].number, output); 716 717 if(0 != output.compare(kNumberFormatTestData[whichLine].string)) { 718 IntlTest::gTest->errln("format().. expected " + kNumberFormatTestData[whichLine].string 719 + " got " + output); 720 goto cleanupAndReturn; 721 } 722 723 // Now check percent. 724 output.remove(); 725 whichLine = (iteration + fOffset)%kPercentFormatTestDataLength; 726 727 percentFormatter->format(kPercentFormatTestData[whichLine].number, output); 728 if(0 != output.compare(kPercentFormatTestData[whichLine].string)) 729 { 730 IntlTest::gTest->errln("percent format().. \n" + 731 showDifference(kPercentFormatTestData[whichLine].string,output)); 732 goto cleanupAndReturn; 733 } 734 735 // Test message error 736 const int kNumberOfMessageTests = 3; 737 UErrorCode statusToCheck; 738 UnicodeString patternToCheck; 739 Locale messageLocale; 740 Locale countryToCheck; 741 double currencyToCheck; 742 743 UnicodeString expected; 744 745 // load the cases. 746 switch((iteration+fOffset) % kNumberOfMessageTests) 747 { 748 default: 749 case 0: 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."; 759 break; 760 case 1: 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."); 770 break; 771 case 2: 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."); 783 break; 784 } 785 786 UnicodeString result; 787 UErrorCode status = U_ZERO_ERROR; 788 formatErrorMessage(status,patternToCheck,messageLocale,statusToCheck, 789 countryToCheck,currencyToCheck,result); 790 if(U_FAILURE(status)) 791 { 792 UnicodeString tmp(u_errorName(status)); 793 IntlTest::gTest->errln("Failure on message format, pattern=" + patternToCheck + 794 ", error = " + tmp); 795 goto cleanupAndReturn; 796 } 797 798 if(result != expected) 799 { 800 IntlTest::gTest->errln("PatternFormat: \n" + showDifference(expected,result)); 801 goto cleanupAndReturn; 802 } 803 // test the Thread Safe Format 804 UnicodeString appendErr; 805 if(!fTSF->doStuff(fNum, appendErr, status)) { 806 IntlTest::gTest->errln(appendErr); 807 goto cleanupAndReturn; 808 } 809 } /* end of for loop */ 810 811 812 813 cleanupAndReturn: 814 fTraceInfo = 2; 815 } 816 817 private: 818 int32_t fOffset; // where we are testing from. 819 }; 820 821 // ** The actual test function. 822 823 void MultithreadTest::TestThreadedIntl() 824 { 825 UnicodeString theErr; 826 827 UErrorCode threadSafeErr = U_ZERO_ERROR; 828 829 ThreadSafeFormatSharedData sharedData(threadSafeErr); 830 assertSuccess("initializing ThreadSafeFormat", threadSafeErr, TRUE); 831 832 // 833 // Create and start the test threads 834 // 835 logln("Spawning: %d threads * %d iterations each.", 836 kFormatThreadThreads, kFormatThreadIterations); 837 FormatThreadTest tests[kFormatThreadThreads]; 838 int32_t j; 839 for(j = 0; j < UPRV_LENGTHOF(tests); j++) { 840 tests[j].fNum = 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); 845 return; 846 } 847 } 848 849 850 for (j=0; j<UPRV_LENGTHOF(tests); j++) { 851 tests[j].join(); 852 logln("Thread # %d is complete..", j); 853 } 854 } 855 #endif /* #if !UCONFIG_NO_FORMATTING */ 856 857 858 859 860 861 //------------------------------------------------------------------------------------------- 862 // 863 // Collation threading test 864 // 865 //------------------------------------------------------------------------------------------- 866 #if !UCONFIG_NO_COLLATION 867 868 #define kCollatorThreadThreads 10 // # of threads to spawn 869 #define kCollatorThreadPatience kCollatorThreadThreads*30 870 871 struct Line { 872 UChar buff[25]; 873 int32_t buflen; 874 } ; 875 876 static UBool 877 skipLineBecauseOfBug(const UChar *s, int32_t length) { 878 // TODO: Fix ICU ticket #8052 879 if(length >= 3 && 880 (s[0] == 0xfb2 || s[0] == 0xfb3) && 881 s[1] == 0x334 && 882 (s[2] == 0xf73 || s[2] == 0xf75 || s[2] == 0xf81)) { 883 return TRUE; 884 } 885 return FALSE; 886 } 887 888 static UCollationResult 889 normalizeResult(int32_t result) { 890 return result<0 ? UCOL_LESS : result==0 ? UCOL_EQUAL : UCOL_GREATER; 891 } 892 893 class CollatorThreadTest : public SimpleThread 894 { 895 private: 896 const Collator *coll; 897 const Line *lines; 898 int32_t noLines; 899 UBool isAtLeastUCA62; 900 public: 901 CollatorThreadTest() : SimpleThread(), 902 coll(NULL), 903 lines(NULL), 904 noLines(0), 905 isAtLeastUCA62(TRUE) 906 { 907 }; 908 void setCollator(Collator *c, Line *l, int32_t nl, UBool atLeastUCA62) 909 { 910 coll = c; 911 lines = l; 912 noLines = nl; 913 isAtLeastUCA62 = atLeastUCA62; 914 } 915 virtual void run() { 916 uint8_t sk1[1024], sk2[1024]; 917 uint8_t *oldSk = NULL, *newSk = sk1; 918 int32_t oldLen = 0; 919 int32_t prev = 0; 920 int32_t i = 0; 921 922 for(i = 0; i < noLines; i++) { 923 if(lines[i].buflen == 0) { continue; } 924 925 if(skipLineBecauseOfBug(lines[i].buff, lines[i].buflen)) { continue; } 926 927 int32_t resLen = coll->getSortKey(lines[i].buff, lines[i].buflen, newSk, 1024); 928 929 if(oldSk != NULL) { 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); 933 934 if(cmpres != -cmpres2) { 935 IntlTest::gTest->errln(UnicodeString("Compare result not symmetrical on line ") + (i + 1)); 936 break; 937 } 938 939 if(cmpres != normalizeResult(skres)) { 940 IntlTest::gTest->errln(UnicodeString("Difference between coll->compare and sortkey compare on line ") + (i + 1)); 941 break; 942 } 943 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. 952 } 953 if(res > 0) { 954 IntlTest::gTest->errln(UnicodeString("Line is not greater or equal than previous line, for line ") + (i + 1)); 955 break; 956 } 957 } 958 959 oldSk = newSk; 960 oldLen = resLen; 961 (void)oldLen; // Suppress set but not used warning. 962 prev = i; 963 964 newSk = (newSk == sk1)?sk2:sk1; 965 } 966 } 967 }; 968 969 void MultithreadTest::TestCollators() 970 { 971 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)); 978 return; 979 } 980 strcat(testDataPath, "CollationTest_"); 981 982 const char* type = "NON_IGNORABLE"; 983 984 const char *ext = ".txt"; 985 if(testFile) { 986 fclose(testFile); 987 } 988 char buffer[1024]; 989 strcpy(buffer, testDataPath); 990 strcat(buffer, type); 991 size_t bufLen = strlen(buffer); 992 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. 998 999 strcpy(buffer+bufLen, ext); 1000 1001 testFile = fopen(buffer, "rb"); 1002 1003 if(testFile == 0) { 1004 strcpy(buffer+bufLen, "_SHORT"); 1005 strcat(buffer, ext); 1006 testFile = fopen(buffer, "rb"); 1007 1008 if(testFile == 0) { 1009 strcpy(buffer+bufLen, "_STUB"); 1010 strcat(buffer, ext); 1011 testFile = fopen(buffer, "rb"); 1012 1013 if (testFile == 0) { 1014 *(buffer+bufLen) = 0; 1015 dataerrln("could not open any of the conformance test files, tried opening base %s", buffer); 1016 return; 1017 } else { 1018 infoln( 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/"); 1023 } 1024 } 1025 } 1026 1027 LocalArray<Line> lines(new Line[200000]); 1028 memset(lines.getAlias(), 0, sizeof(Line)*200000); 1029 int32_t lineNum = 0; 1030 1031 UChar bufferU[1024]; 1032 uint32_t first = 0; 1033 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; 1040 } else { 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; 1045 } 1046 lineNum++; 1047 } 1048 fclose(testFile); 1049 if(U_FAILURE(status)) { 1050 dataerrln("Couldn't read the test file!"); 1051 return; 1052 } 1053 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; 1058 1059 LocalPointer<Collator> coll(Collator::createInstance(Locale::getRoot(), status)); 1060 if(U_FAILURE(status)) { 1061 errcheckln(status, "Couldn't open UCA collator"); 1062 return; 1063 } 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); 1069 1070 int32_t spawnResult = 0; 1071 LocalArray<CollatorThreadTest> tests(new CollatorThreadTest[kCollatorThreadThreads]); 1072 1073 logln(UnicodeString("Spawning: ") + kCollatorThreadThreads + " threads * " + kFormatThreadIterations + " iterations each."); 1074 int32_t j = 0; 1075 for(j = 0; j < kCollatorThreadThreads; j++) { 1076 //logln("Setting collator %i", j); 1077 tests[j].setCollator(coll.getAlias(), lines.getAlias(), lineNum, isAtLeastUCA62); 1078 } 1079 for(j = 0; j < kCollatorThreadThreads; j++) { 1080 log("%i ", 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); 1084 return; 1085 } 1086 } 1087 logln("Spawned all"); 1088 1089 for(int32_t i=0;i<kCollatorThreadThreads;i++) { 1090 tests[i].join(); 1091 //logln(UnicodeString("Test #") + i + " is complete.. "); 1092 } 1093 } 1094 1095 #endif /* #if !UCONFIG_NO_COLLATION */ 1096 1097 1098 1099 1100 //------------------------------------------------------------------------------------------- 1101 // 1102 // StringThreadTest2 1103 // 1104 //------------------------------------------------------------------------------------------- 1105 1106 const int kStringThreadIterations = 2500;// # of iterations per thread 1107 const int kStringThreadThreads = 10; // # of threads to spawn 1108 1109 1110 class StringThreadTest2 : public SimpleThread 1111 { 1112 public: 1113 int fNum; 1114 int fTraceInfo; 1115 static const UnicodeString *gSharedString; 1116 1117 StringThreadTest2() // constructor is NOT multithread safe. 1118 : SimpleThread(), 1119 fTraceInfo(0) 1120 { 1121 }; 1122 1123 1124 virtual void run() 1125 { 1126 fTraceInfo = 1; 1127 int loopCount = 0; 1128 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__); 1132 break; 1133 } 1134 UnicodeString s1 = *gSharedString; 1135 s1 += "cat this"; 1136 UnicodeString s2(s1); 1137 UnicodeString s3 = *gSharedString; 1138 s2 = s3; 1139 s3.truncate(12); 1140 s2.truncate(0); 1141 } 1142 1143 fTraceInfo = 2; 1144 } 1145 1146 }; 1147 1148 const UnicodeString *StringThreadTest2::gSharedString = NULL; 1149 1150 // ** The actual test function. 1151 1152 1153 void MultithreadTest::TestString() 1154 { 1155 int j; 1156 StringThreadTest2::gSharedString = new UnicodeString("This is the original test string."); 1157 StringThreadTest2 tests[kStringThreadThreads]; 1158 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); 1164 } 1165 } 1166 1167 // Force a failure, to verify test is functioning and can report errors. 1168 // const_cast<UnicodeString *>(StringThreadTest2::gSharedString)->setCharAt(5, 'x'); 1169 1170 for(j=0; j<kStringThreadThreads; j++) { 1171 tests[j].join(); 1172 logln(UnicodeString("Test #") + j + " is complete.. "); 1173 } 1174 1175 delete StringThreadTest2::gSharedString; 1176 StringThreadTest2::gSharedString = NULL; 1177 } 1178 1179 1180 // 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. 1185 // 1186 1187 #if !UCONFIG_NO_TRANSLITERATION 1188 Transliterator *gSharedTranslit = NULL; 1189 class TxThread: public SimpleThread { 1190 public: 1191 TxThread() {}; 1192 ~TxThread(); 1193 void run(); 1194 }; 1195 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). 1202 { 1203 IntlTest::gTest->errln("%s:%d Transliteration failed.", __FILE__, __LINE__); 1204 } 1205 } 1206 #endif 1207 1208 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)); 1215 return; 1216 } 1217 gSharedTranslit = tx.getAlias(); 1218 TxThread threads[4]; 1219 int32_t i; 1220 for (i=0; i<UPRV_LENGTHOF(threads); i++) { 1221 threads[i].start(); 1222 } 1223 1224 for (i=0; i<UPRV_LENGTHOF(threads); i++) { 1225 threads[i].join(); 1226 } 1227 gSharedTranslit = NULL; 1228 #endif // !UCONFIG_NO_TRANSLITERATION 1229 } 1230 1231 1232 // 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. 1241 // Exits. 1242 // 1243 1244 class CondThread: public SimpleThread { 1245 public: 1246 CondThread() :fFinished(false) {}; 1247 ~CondThread() {}; 1248 void run(); 1249 bool fFinished; 1250 }; 1251 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; 1259 1260 1261 // Worker thread function. 1262 void CondThread::run() { 1263 umtx_lock(&gCTMutex); 1264 gStartedThreads += gConditionTestOne; 1265 umtx_condBroadcast(&gCTConditionVar); 1266 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); 1271 } 1272 umtx_condWait(&gCTConditionVar, &gCTMutex); 1273 } 1274 1275 gFinishedThreads += gConditionTestOne; 1276 fFinished = true; 1277 umtx_condBroadcast(&gCTConditionVar); 1278 1279 while (gFinishedThreads < NUMTHREADS) { 1280 umtx_condWait(&gCTConditionVar, &gCTMutex); 1281 } 1282 umtx_unlock(&gCTMutex); 1283 } 1284 1285 void MultithreadTest::TestConditionVariables() { 1286 gStartedThreads = 0; 1287 gFinishedThreads = 0; 1288 int i; 1289 1290 umtx_lock(&gCTMutex); 1291 CondThread *threads[NUMTHREADS]; 1292 for (i=0; i<NUMTHREADS; ++i) { 1293 threads[i] = new CondThread; 1294 threads[i]->start(); 1295 } 1296 1297 while (gStartedThreads < NUMTHREADS) { 1298 umtx_condWait(&gCTConditionVar, &gCTMutex); 1299 } 1300 1301 while (gFinishedThreads < NUMTHREADS) { 1302 umtx_condWait(&gCTConditionVar, &gCTMutex); 1303 } 1304 1305 umtx_unlock(&gCTMutex); 1306 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); 1310 } 1311 } 1312 1313 for (i=0; i<NUMTHREADS; ++i) { 1314 threads[i]->join(); 1315 delete threads[i]; 1316 } 1317 } 1318 1319 1320 // 1321 // Unified Cache Test 1322 // 1323 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 1327 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"}; 1336 1337 static int32_t gObjectsCreated = 0; // protected by gCTMutex 1338 static const int32_t CACHE_LOAD = 3; 1339 1340 class UCTMultiThreadItem : public SharedObject { 1341 public: 1342 char *value; 1343 UCTMultiThreadItem(const char *x) : value(NULL) { 1344 value = uprv_strdup(x); 1345 } 1346 virtual ~UCTMultiThreadItem() { 1347 uprv_free(value); 1348 } 1349 }; 1350 1351 U_NAMESPACE_BEGIN 1352 1353 template<> U_EXPORT 1354 const UCTMultiThreadItem *LocaleCacheKey<UCTMultiThreadItem>::createObject( 1355 const void *context, UErrorCode &status) const { 1356 const UnifiedCache *cacheContext = (const UnifiedCache *) context; 1357 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); 1362 return result; 1363 } 1364 cacheContext->get(LocaleCacheKey<UCTMultiThreadItem>(fLoc.getLanguage()), result, status); 1365 return result; 1366 } 1367 1368 umtx_lock(&gCTMutex); 1369 bool firstObject = (gObjectsCreated == 0); 1370 if (firstObject) { 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. 1374 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); 1380 } 1381 } 1382 umtx_unlock(&gCTMutex); 1383 1384 const UCTMultiThreadItem *result = 1385 new UCTMultiThreadItem(fLoc.getLanguage()); 1386 if (result == NULL) { 1387 status = U_MEMORY_ALLOCATION_ERROR; 1388 } else { 1389 result->addRef(); 1390 } 1391 1392 // Log that we created an object. The first object was already counted, 1393 // don't do it again. 1394 umtx_lock(&gCTMutex); 1395 if (!firstObject) { 1396 gObjectsCreated += 1; 1397 } 1398 umtx_condBroadcast(&gCTConditionVar); 1399 umtx_unlock(&gCTMutex); 1400 1401 return result; 1402 } 1403 1404 U_NAMESPACE_END 1405 1406 class UnifiedCacheThread: public SimpleThread { 1407 public: 1408 UnifiedCacheThread( 1409 const UnifiedCache *cache, 1410 const char *loc, 1411 const char *loc2) : fCache(cache), fLoc(loc), fLoc2(loc2) {}; 1412 ~UnifiedCacheThread() {}; 1413 void run(); 1414 void exerciseByLocale(const Locale &); 1415 const UnifiedCache *fCache; 1416 Locale fLoc; 1417 Locale fLoc2; 1418 }; 1419 1420 void UnifiedCacheThread::exerciseByLocale(const Locale &locale) { 1421 UErrorCode status = U_ZERO_ERROR; 1422 const UCTMultiThreadItem *origItem = NULL; 1423 fCache->get( 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(), 1430 origItem->value); 1431 } 1432 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; 1437 fCache->get( 1438 LocaleCacheKey<UCTMultiThreadItem>(locale), fCache, item, status); 1439 if (item != origItem) { 1440 IntlTest::gTest->errln( 1441 "%s:%d Expected to get the same pointer", 1442 __FILE__, 1443 __LINE__); 1444 } 1445 if (item != NULL) { 1446 item->removeRef(); 1447 } 1448 } 1449 origItem->removeRef(); 1450 } 1451 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); 1458 } 1459 1460 void MultithreadTest::TestUnifiedCache() { 1461 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 1464 // values 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)); 1470 1471 gFinishedThreads = 0; 1472 gObjectsCreated = 0; 1473 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(); 1481 } 1482 } 1483 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(); 1487 } 1488 } 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__); 1495 } 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)); 1502 1503 } 1504 1505 assertEquals("unused values", 2, cache.unusedCount()); 1506 1507 // clean up threads 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]; 1511 } 1512 } 1513 } 1514 1515 #if !UCONFIG_NO_TRANSLITERATION 1516 // 1517 // BreakTransliterator Threading Test 1518 // This is a test for bug #11603. Test verified to fail prior to fix. 1519 // 1520 1521 static const Transliterator *gSharedTransliterator; 1522 static const UnicodeString *gTranslitInput; 1523 static const UnicodeString *gTranslitExpected; 1524 1525 class BreakTranslitThread: public SimpleThread { 1526 public: 1527 BreakTranslitThread() {}; 1528 ~BreakTranslitThread() {}; 1529 void run(); 1530 }; 1531 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__); 1538 break; 1539 } 1540 } 1541 } 1542 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; 1549 1550 gSharedTransliterator = Transliterator::createInstance( 1551 UNICODE_STRING_SIMPLE("Any-Latin; Lower; NFD; [:Diacritic:]Remove; NFC; Latin-ASCII;"), UTRANS_FORWARD, status); 1552 if (!gSharedTransliterator) { 1553 return; 1554 } 1555 TSMTHREAD_ASSERT_SUCCESS(status); 1556 1557 UnicodeString expected(*gTranslitInput); 1558 gSharedTransliterator->transliterate(expected); 1559 gTranslitExpected = &expected; 1560 1561 BreakTranslitThread threads[4]; 1562 for (int i=0; i<UPRV_LENGTHOF(threads); ++i) { 1563 threads[i].start(); 1564 } 1565 for (int i=0; i<UPRV_LENGTHOF(threads); ++i) { 1566 threads[i].join(); 1567 } 1568 1569 delete gSharedTransliterator; 1570 gTranslitInput = NULL; 1571 gTranslitExpected = NULL; 1572 } 1573 1574 #endif /* !UCONFIG_NO_TRANSLITERATION */ 1575