| OLD | NEW |
| (Empty) |
| 1 /******************************************************************** | |
| 2 * COPYRIGHT: | |
| 3 * Copyright (c) 1999-2015, International Business Machines Corporation and | |
| 4 * others. All Rights Reserved. | |
| 5 ********************************************************************/ | |
| 6 | |
| 7 #include "simplethread.h" | |
| 8 | |
| 9 #include "unicode/utypes.h" | |
| 10 #include "unicode/ustring.h" | |
| 11 #include "umutex.h" | |
| 12 #include "cmemory.h" | |
| 13 #include "cstring.h" | |
| 14 #include "uparse.h" | |
| 15 #include "unicode/localpointer.h" | |
| 16 #include "unicode/resbund.h" | |
| 17 #include "unicode/udata.h" | |
| 18 #include "unicode/uloc.h" | |
| 19 #include "unicode/locid.h" | |
| 20 #include "putilimp.h" | |
| 21 #include "intltest.h" | |
| 22 #include "tsmthred.h" | |
| 23 #include "unicode/ushape.h" | |
| 24 #include "unicode/translit.h" | |
| 25 #include "sharedobject.h" | |
| 26 #include "unifiedcache.h" | |
| 27 #include "uassert.h" | |
| 28 | |
| 29 | |
| 30 #define TSMTHREAD_FAIL(msg) errln("%s at file %s, line %d", msg, __FILE__, __LIN
E__) | |
| 31 #define TSMTHREAD_ASSERT(expr) {if (!(expr)) {TSMTHREAD_FAIL("Fail");}} | |
| 32 #define TSMTHREAD_ASSERT_SUCCESS(status) {if (U_FAILURE(status)) { \ | |
| 33 errln("file: %s:%d status = %s\n", __FILE__, __LINE__, u_error
Name(status));}} | |
| 34 | |
| 35 MultithreadTest::MultithreadTest() | |
| 36 { | |
| 37 } | |
| 38 | |
| 39 MultithreadTest::~MultithreadTest() | |
| 40 { | |
| 41 } | |
| 42 | |
| 43 #include <stdio.h> | |
| 44 #include <string.h> | |
| 45 #include <ctype.h> // tolower, toupper | |
| 46 | |
| 47 #include "unicode/putil.h" | |
| 48 | |
| 49 // for mthreadtest | |
| 50 #include "unicode/numfmt.h" | |
| 51 #include "unicode/choicfmt.h" | |
| 52 #include "unicode/msgfmt.h" | |
| 53 #include "unicode/locid.h" | |
| 54 #include "unicode/coll.h" | |
| 55 #include "unicode/calendar.h" | |
| 56 #include "ucaconf.h" | |
| 57 | |
| 58 | |
| 59 void MultithreadTest::runIndexedTest( int32_t index, UBool exec, | |
| 60 const char* &name, char* /*par*/ ) { | |
| 61 if (exec) | |
| 62 logln("TestSuite MultithreadTest: "); | |
| 63 switch (index) { | |
| 64 case 0: | |
| 65 name = "TestThreads"; | |
| 66 if (exec) | |
| 67 TestThreads(); | |
| 68 break; | |
| 69 | |
| 70 case 1: | |
| 71 name = "TestMutex"; | |
| 72 if (exec) | |
| 73 TestMutex(); | |
| 74 break; | |
| 75 | |
| 76 case 2: | |
| 77 name = "TestThreadedIntl"; | |
| 78 #if !UCONFIG_NO_FORMATTING | |
| 79 if (exec) { | |
| 80 TestThreadedIntl(); | |
| 81 } | |
| 82 #endif | |
| 83 break; | |
| 84 | |
| 85 case 3: | |
| 86 name = "TestCollators"; | |
| 87 #if !UCONFIG_NO_COLLATION | |
| 88 if (exec) { | |
| 89 TestCollators(); | |
| 90 } | |
| 91 #endif /* #if !UCONFIG_NO_COLLATION */ | |
| 92 break; | |
| 93 | |
| 94 case 4: | |
| 95 name = "TestString"; | |
| 96 if (exec) { | |
| 97 TestString(); | |
| 98 } | |
| 99 break; | |
| 100 | |
| 101 case 5: | |
| 102 name = "TestArabicShapingThreads"; | |
| 103 if (exec) { | |
| 104 TestArabicShapingThreads(); | |
| 105 } | |
| 106 break; | |
| 107 | |
| 108 case 6: | |
| 109 name = "TestAnyTranslit"; | |
| 110 if (exec) { | |
| 111 TestAnyTranslit(); | |
| 112 } | |
| 113 break; | |
| 114 | |
| 115 case 7: | |
| 116 name = "TestConditionVariables"; | |
| 117 if (exec) { | |
| 118 TestConditionVariables(); | |
| 119 } | |
| 120 break; | |
| 121 case 8: | |
| 122 name = "TestUnifiedCache"; | |
| 123 if (exec) { | |
| 124 TestUnifiedCache(); | |
| 125 } | |
| 126 break; | |
| 127 #if !UCONFIG_NO_TRANSLITERATION | |
| 128 case 9: | |
| 129 name = "TestBreakTranslit"; | |
| 130 if (exec) { | |
| 131 TestBreakTranslit(); | |
| 132 } | |
| 133 break; | |
| 134 #endif | |
| 135 default: | |
| 136 name = ""; | |
| 137 break; //needed to end loop | |
| 138 } | |
| 139 } | |
| 140 | |
| 141 | |
| 142 //------------------------------------------------------------------------------
----- | |
| 143 // | |
| 144 // TestThreads -- see if threads really work at all. | |
| 145 // | |
| 146 // Set up N threads pointing at N chars. When they are started, they will | |
| 147 // set their chars. At the end we make sure they are all set. | |
| 148 // | |
| 149 //------------------------------------------------------------------------------
----- | |
| 150 | |
| 151 class TestThreadsThread : public SimpleThread | |
| 152 { | |
| 153 public: | |
| 154 TestThreadsThread(char* whatToChange) { fWhatToChange = whatToChange; } | |
| 155 virtual void run() { Mutex m; | |
| 156 *fWhatToChange = '*'; | |
| 157 } | |
| 158 private: | |
| 159 char *fWhatToChange; | |
| 160 }; | |
| 161 | |
| 162 | |
| 163 void MultithreadTest::TestThreads() | |
| 164 { | |
| 165 static const int32_t THREADTEST_NRTHREADS = 8; | |
| 166 char threadTestChars[THREADTEST_NRTHREADS + 1]; | |
| 167 SimpleThread *threads[THREADTEST_NRTHREADS]; | |
| 168 int32_t numThreadsStarted = 0; | |
| 169 | |
| 170 int32_t i; | |
| 171 for(i=0;i<THREADTEST_NRTHREADS;i++) | |
| 172 { | |
| 173 threadTestChars[i] = ' '; | |
| 174 threads[i] = new TestThreadsThread(&threadTestChars[i]); | |
| 175 } | |
| 176 threadTestChars[THREADTEST_NRTHREADS] = '\0'; | |
| 177 | |
| 178 logln("->" + UnicodeString(threadTestChars) + "<- Firing off threads.. "); | |
| 179 for(i=0;i<THREADTEST_NRTHREADS;i++) | |
| 180 { | |
| 181 if (threads[i]->start() != 0) { | |
| 182 errln("Error starting thread %d", i); | |
| 183 } | |
| 184 else { | |
| 185 numThreadsStarted++; | |
| 186 } | |
| 187 logln(" Subthread started."); | |
| 188 } | |
| 189 | |
| 190 if (numThreadsStarted != THREADTEST_NRTHREADS) { | |
| 191 errln("Not all threads could be started for testing!"); | |
| 192 return; | |
| 193 } | |
| 194 | |
| 195 logln("Waiting for threads to be set.."); | |
| 196 for(i=0; i<THREADTEST_NRTHREADS; i++) { | |
| 197 threads[i]->join(); | |
| 198 if (threadTestChars[i] != '*') { | |
| 199 errln("%s:%d Thread %d failed.", __FILE__, __LINE__, i); | |
| 200 } | |
| 201 delete threads[i]; | |
| 202 } | |
| 203 } | |
| 204 | |
| 205 | |
| 206 //------------------------------------------------------------------------------
----- | |
| 207 // | |
| 208 // TestArabicShapeThreads -- see if calls to u_shapeArabic in many threads wor
ks successfully | |
| 209 // | |
| 210 // Set up N threads pointing at N chars. When they are started, they will make
calls to doTailTest which tests | |
| 211 // u_shapeArabic, if the calls are successful it will the set * chars. | |
| 212 // At the end we make sure all threads managed to run u_shapeArabic successful
ly. | |
| 213 // This is a unit test for ticket 9473 | |
| 214 // | |
| 215 //------------------------------------------------------------------------------
----- | |
| 216 | |
| 217 class TestArabicShapeThreads : public SimpleThread | |
| 218 { | |
| 219 public: | |
| 220 TestArabicShapeThreads() {}; | |
| 221 virtual void run() { doTailTest(); }; | |
| 222 private: | |
| 223 void doTailTest(); | |
| 224 }; | |
| 225 | |
| 226 | |
| 227 void TestArabicShapeThreads::doTailTest(void) { | |
| 228 static const UChar src[] = { 0x0020, 0x0633, 0 }; | |
| 229 static const UChar dst_old[] = { 0xFEB1, 0x200B,0 }; | |
| 230 static const UChar dst_new[] = { 0xFEB1, 0xFE73,0 }; | |
| 231 UChar dst[3] = { 0x0000, 0x0000,0 }; | |
| 232 int32_t length; | |
| 233 UErrorCode status; | |
| 234 | |
| 235 for (int32_t loopCount = 0; loopCount < 100; loopCount++) { | |
| 236 status = U_ZERO_ERROR; | |
| 237 length = u_shapeArabic(src, -1, dst, UPRV_LENGTHOF(dst), | |
| 238 U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR, &status); | |
| 239 if(U_FAILURE(status)) { | |
| 240 IntlTest::gTest->errln("Fail: status %s\n", u_errorName(status)); | |
| 241 return; | |
| 242 } else if(length!=2) { | |
| 243 IntlTest::gTest->errln("Fail: len %d expected 3\n", length); | |
| 244 return; | |
| 245 } else if(u_strncmp(dst,dst_old,UPRV_LENGTHOF(dst))) { | |
| 246 IntlTest::gTest->errln("Fail: got U+%04X U+%04X expected U+%04X U+%0
4X\n", | |
| 247 dst[0],dst[1],dst_old[0],dst_old[1]); | |
| 248 return; | |
| 249 } | |
| 250 | |
| 251 | |
| 252 //"Trying new tail | |
| 253 status = U_ZERO_ERROR; | |
| 254 length = u_shapeArabic(src, -1, dst, UPRV_LENGTHOF(dst), | |
| 255 U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR|U_SHAPE_TAIL_NEW
_UNICODE, &status); | |
| 256 if(U_FAILURE(status)) { | |
| 257 IntlTest::gTest->errln("Fail: status %s\n", u_errorName(status)); | |
| 258 return; | |
| 259 } else if(length!=2) { | |
| 260 IntlTest::gTest->errln("Fail: len %d expected 3\n", length); | |
| 261 return; | |
| 262 } else if(u_strncmp(dst,dst_new,UPRV_LENGTHOF(dst))) { | |
| 263 IntlTest::gTest->errln("Fail: got U+%04X U+%04X expected U+%04X U+%0
4X\n", | |
| 264 dst[0],dst[1],dst_new[0],dst_new[1]); | |
| 265 return; | |
| 266 } | |
| 267 } | |
| 268 return; | |
| 269 } | |
| 270 | |
| 271 | |
| 272 void MultithreadTest::TestArabicShapingThreads() | |
| 273 { | |
| 274 TestArabicShapeThreads threads[30]; | |
| 275 | |
| 276 int32_t i; | |
| 277 | |
| 278 logln("-> do TestArabicShapingThreads <- Firing off threads.. "); | |
| 279 for(i=0; i < UPRV_LENGTHOF(threads); i++) { | |
| 280 if (threads[i].start() != 0) { | |
| 281 errln("Error starting thread %d", i); | |
| 282 } | |
| 283 } | |
| 284 | |
| 285 for(i=0; i < UPRV_LENGTHOF(threads); i++) { | |
| 286 threads[i].join(); | |
| 287 } | |
| 288 logln("->TestArabicShapingThreads <- Got all threads! cya"); | |
| 289 } | |
| 290 | |
| 291 | |
| 292 //----------------------------------------------------------------------- | |
| 293 // | |
| 294 // TestMutex - a simple (non-stress) test to verify that ICU mutexes | |
| 295 // and condition variables are functioning. Does not test the use
of | |
| 296 // mutexes within ICU services, but rather that the | |
| 297 // platform's mutex support is at least superficially there. | |
| 298 // | |
| 299 //---------------------------------------------------------------------- | |
| 300 static UMutex gTestMutexA = U_MUTEX_INITIALIZER; | |
| 301 static UConditionVar gThreadsCountChanged = U_CONDITION_INITIALIZER; | |
| 302 | |
| 303 static int gThreadsStarted = 0; | |
| 304 static int gThreadsInMiddle = 0; | |
| 305 static int gThreadsDone = 0; | |
| 306 | |
| 307 static const int TESTMUTEX_THREAD_COUNT = 40; | |
| 308 | |
| 309 class TestMutexThread : public SimpleThread | |
| 310 { | |
| 311 public: | |
| 312 virtual void run() { | |
| 313 // This is the code that each of the spawned threads runs. | |
| 314 // All threads move together throught the started - middle - done sequen
ce together, | |
| 315 // waiting for all other threads to reach each point before advancing. | |
| 316 umtx_lock(&gTestMutexA); | |
| 317 gThreadsStarted += 1; | |
| 318 umtx_condBroadcast(&gThreadsCountChanged); | |
| 319 while (gThreadsStarted < TESTMUTEX_THREAD_COUNT) { | |
| 320 if (gThreadsInMiddle != 0) { | |
| 321 IntlTest::gTest->errln( | |
| 322 "%s:%d gThreadsInMiddle = %d. Expected 0.", __FILE__, __LINE
__, gThreadsInMiddle); | |
| 323 return; | |
| 324 } | |
| 325 umtx_condWait(&gThreadsCountChanged, &gTestMutexA); | |
| 326 } | |
| 327 | |
| 328 gThreadsInMiddle += 1; | |
| 329 umtx_condBroadcast(&gThreadsCountChanged); | |
| 330 while (gThreadsInMiddle < TESTMUTEX_THREAD_COUNT) { | |
| 331 if (gThreadsDone != 0) { | |
| 332 IntlTest::gTest->errln( | |
| 333 "%s:%d gThreadsDone = %d. Expected 0.", __FILE__, __LINE__,
gThreadsDone); | |
| 334 return; | |
| 335 } | |
| 336 umtx_condWait(&gThreadsCountChanged, &gTestMutexA); | |
| 337 } | |
| 338 | |
| 339 gThreadsDone += 1; | |
| 340 umtx_condBroadcast(&gThreadsCountChanged); | |
| 341 while (gThreadsDone < TESTMUTEX_THREAD_COUNT) { | |
| 342 umtx_condWait(&gThreadsCountChanged, &gTestMutexA); | |
| 343 } | |
| 344 umtx_unlock(&gTestMutexA); | |
| 345 } | |
| 346 }; | |
| 347 | |
| 348 void MultithreadTest::TestMutex() | |
| 349 { | |
| 350 gThreadsStarted = 0; | |
| 351 gThreadsInMiddle = 0; | |
| 352 gThreadsDone = 0; | |
| 353 int32_t i = 0; | |
| 354 TestMutexThread threads[TESTMUTEX_THREAD_COUNT]; | |
| 355 umtx_lock(&gTestMutexA); | |
| 356 for (i=0; i<TESTMUTEX_THREAD_COUNT; i++) { | |
| 357 if (threads[i].start() != 0) { | |
| 358 errln("%s:%d Error starting thread %d", __FILE__, __LINE__, i); | |
| 359 return; | |
| 360 } | |
| 361 } | |
| 362 | |
| 363 // Because we are holding gTestMutexA, all of the threads should be blocked | |
| 364 // at the start of their run() function. | |
| 365 if (gThreadsStarted != 0) { | |
| 366 errln("%s:%d gThreadsStarted=%d. Expected 0.", __FILE__, __LINE__, gThre
adsStarted); | |
| 367 return; | |
| 368 } | |
| 369 | |
| 370 while (gThreadsInMiddle < TESTMUTEX_THREAD_COUNT) { | |
| 371 if (gThreadsDone != 0) { | |
| 372 errln("%s:%d gThreadsDone=%d. Expected 0.", __FILE__, __LINE__, gThr
eadsStarted); | |
| 373 return; | |
| 374 } | |
| 375 umtx_condWait(&gThreadsCountChanged, &gTestMutexA); | |
| 376 } | |
| 377 | |
| 378 while (gThreadsDone < TESTMUTEX_THREAD_COUNT) { | |
| 379 umtx_condWait(&gThreadsCountChanged, &gTestMutexA); | |
| 380 } | |
| 381 umtx_unlock(&gTestMutexA); | |
| 382 | |
| 383 for (i=0; i<TESTMUTEX_THREAD_COUNT; i++) { | |
| 384 threads[i].join(); | |
| 385 } | |
| 386 } | |
| 387 | |
| 388 | |
| 389 //------------------------------------------------------------------------------
------------- | |
| 390 // | |
| 391 // TestMultithreadedIntl. Test ICU Formatting in a multi-threaded environment | |
| 392 // | |
| 393 //------------------------------------------------------------------------------
------------- | |
| 394 | |
| 395 | |
| 396 // * Show exactly where the string's differences lie. | |
| 397 UnicodeString showDifference(const UnicodeString& expected, const UnicodeString&
result) | |
| 398 { | |
| 399 UnicodeString res; | |
| 400 res = expected + "<Expected\n"; | |
| 401 if(expected.length() != result.length()) | |
| 402 res += " [ Different lengths ] \n"; | |
| 403 else | |
| 404 { | |
| 405 for(int32_t i=0;i<expected.length();i++) | |
| 406 { | |
| 407 if(expected[i] == result[i]) | |
| 408 { | |
| 409 res += " "; | |
| 410 } | |
| 411 else | |
| 412 { | |
| 413 res += "|"; | |
| 414 } | |
| 415 } | |
| 416 res += "<Differences"; | |
| 417 res += "\n"; | |
| 418 } | |
| 419 res += result + "<Result\n"; | |
| 420 | |
| 421 return res; | |
| 422 } | |
| 423 | |
| 424 | |
| 425 //------------------------------------------------------------------------------
------------- | |
| 426 // | |
| 427 // FormatThreadTest - a thread that tests performing a number of numberformats
. | |
| 428 // | |
| 429 //------------------------------------------------------------------------------
------------- | |
| 430 | |
| 431 const int kFormatThreadIterations = 100; // # of iterations per thread | |
| 432 const int kFormatThreadThreads = 10; // # of threads to spawn | |
| 433 | |
| 434 #if !UCONFIG_NO_FORMATTING | |
| 435 | |
| 436 | |
| 437 | |
| 438 struct FormatThreadTestData | |
| 439 { | |
| 440 double number; | |
| 441 UnicodeString string; | |
| 442 FormatThreadTestData(double a, const UnicodeString& b) : number(a),string(b)
{} | |
| 443 } ; | |
| 444 | |
| 445 | |
| 446 // "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is co
sting {3 number,currency}." | |
| 447 | |
| 448 static void formatErrorMessage(UErrorCode &realStatus, const UnicodeString& patt
ern, const Locale& theLocale, | |
| 449 UErrorCode inStatus0, /* statusString 1 */ const Locale &in
Country2, double currency3, // these numbers are the message arguments. | |
| 450 UnicodeString &result) | |
| 451 { | |
| 452 if(U_FAILURE(realStatus)) | |
| 453 return; // you messed up | |
| 454 | |
| 455 UnicodeString errString1(u_errorName(inStatus0)); | |
| 456 | |
| 457 UnicodeString countryName2; | |
| 458 inCountry2.getDisplayCountry(theLocale,countryName2); | |
| 459 | |
| 460 Formattable myArgs[] = { | |
| 461 Formattable((int32_t)inStatus0), // inStatus0 {0} | |
| 462 Formattable(errString1), // statusString1 {1} | |
| 463 Formattable(countryName2), // inCountry2 {2} | |
| 464 Formattable(currency3)// currency3 {3,number,currency} | |
| 465 }; | |
| 466 | |
| 467 MessageFormat *fmt = new MessageFormat("MessageFormat's API is broken!!!!!!!
!!!!",realStatus); | |
| 468 fmt->setLocale(theLocale); | |
| 469 fmt->applyPattern(pattern, realStatus); | |
| 470 | |
| 471 if (U_FAILURE(realStatus)) { | |
| 472 delete fmt; | |
| 473 return; | |
| 474 } | |
| 475 | |
| 476 FieldPosition ignore = 0; | |
| 477 fmt->format(myArgs,4,result,ignore,realStatus); | |
| 478 | |
| 479 delete fmt; | |
| 480 } | |
| 481 | |
| 482 /** | |
| 483 * Shared formatters & data used by instances of ThreadSafeFormat. | |
| 484 * Exactly one instance of this class is created, and it is then shared concurr
ently | |
| 485 * by the multiple instances of ThreadSafeFormat. | |
| 486 */ | |
| 487 class ThreadSafeFormatSharedData { | |
| 488 public: | |
| 489 ThreadSafeFormatSharedData(UErrorCode &status); | |
| 490 ~ThreadSafeFormatSharedData(); | |
| 491 LocalPointer<NumberFormat> fFormat; | |
| 492 Formattable fYDDThing; | |
| 493 Formattable fBBDThing; | |
| 494 UnicodeString fYDDStr; | |
| 495 UnicodeString fBBDStr; | |
| 496 }; | |
| 497 | |
| 498 const ThreadSafeFormatSharedData *gSharedData = NULL; | |
| 499 | |
| 500 ThreadSafeFormatSharedData::ThreadSafeFormatSharedData(UErrorCode &status) { | |
| 501 fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), s
tatus)); | |
| 502 static const UChar kYDD[] = { 0x59, 0x44, 0x44, 0x00 }; | |
| 503 static const UChar kBBD[] = { 0x42, 0x42, 0x44, 0x00 }; | |
| 504 fYDDThing.adoptObject(new CurrencyAmount(123.456, kYDD, status)); | |
| 505 fBBDThing.adoptObject(new CurrencyAmount(987.654, kBBD, status)); | |
| 506 if (U_FAILURE(status)) { | |
| 507 return; | |
| 508 } | |
| 509 fFormat->format(fYDDThing, fYDDStr, NULL, status); | |
| 510 fFormat->format(fBBDThing, fBBDStr, NULL, status); | |
| 511 gSharedData = this; | |
| 512 } | |
| 513 | |
| 514 ThreadSafeFormatSharedData::~ThreadSafeFormatSharedData() { | |
| 515 gSharedData = NULL; | |
| 516 } | |
| 517 | |
| 518 /** | |
| 519 * Class for thread-safe testing of format. | |
| 520 * Instances of this class appear as members of class FormatThreadTest. | |
| 521 * Multiple instances of FormatThreadTest coexist. | |
| 522 * ThreadSafeFormat::doStuff() is called concurrently to test the thread safet
y of | |
| 523 * various shared format operations. | |
| 524 */ | |
| 525 class ThreadSafeFormat { | |
| 526 public: | |
| 527 /* give a unique offset to each thread */ | |
| 528 ThreadSafeFormat(UErrorCode &status); | |
| 529 UBool doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) co
nst; | |
| 530 private: | |
| 531 LocalPointer<NumberFormat> fFormat; // formatter - en_US constructed currency | |
| 532 }; | |
| 533 | |
| 534 | |
| 535 ThreadSafeFormat::ThreadSafeFormat(UErrorCode &status) { | |
| 536 fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), sta
tus)); | |
| 537 } | |
| 538 | |
| 539 static const UChar kUSD[] = { 0x55, 0x53, 0x44, 0x00 }; | |
| 540 | |
| 541 UBool ThreadSafeFormat::doStuff(int32_t offset, UnicodeString &appendErr, UError
Code &status) const { | |
| 542 UBool okay = TRUE; | |
| 543 | |
| 544 if(u_strcmp(fFormat->getCurrency(), kUSD)) { | |
| 545 appendErr.append("fFormat currency != ") | |
| 546 .append(kUSD) | |
| 547 .append(", =") | |
| 548 .append(fFormat->getCurrency()) | |
| 549 .append("! "); | |
| 550 okay = FALSE; | |
| 551 } | |
| 552 | |
| 553 if(u_strcmp(gSharedData->fFormat->getCurrency(), kUSD)) { | |
| 554 appendErr.append("gFormat currency != ") | |
| 555 .append(kUSD) | |
| 556 .append(", =") | |
| 557 .append(gSharedData->fFormat->getCurrency()) | |
| 558 .append("! "); | |
| 559 okay = FALSE; | |
| 560 } | |
| 561 UnicodeString str; | |
| 562 const UnicodeString *o=NULL; | |
| 563 Formattable f; | |
| 564 const NumberFormat *nf = NULL; // only operate on it as const. | |
| 565 switch(offset%4) { | |
| 566 case 0: f = gSharedData->fYDDThing; o = &gSharedData->fYDDStr; nf = gShared
Data->fFormat.getAlias(); break; | |
| 567 case 1: f = gSharedData->fBBDThing; o = &gSharedData->fBBDStr; nf = gShared
Data->fFormat.getAlias(); break; | |
| 568 case 2: f = gSharedData->fYDDThing; o = &gSharedData->fYDDStr; nf = fFormat
.getAlias(); break; | |
| 569 case 3: f = gSharedData->fBBDThing; o = &gSharedData->fBBDStr; nf = fFormat
.getAlias(); break; | |
| 570 } | |
| 571 nf->format(f, str, NULL, status); | |
| 572 | |
| 573 if(*o != str) { | |
| 574 appendErr.append(showDifference(*o, str)); | |
| 575 okay = FALSE; | |
| 576 } | |
| 577 return okay; | |
| 578 } | |
| 579 | |
| 580 UBool U_CALLCONV isAcceptable(void *, const char *, const char *, const UDataInf
o *) { | |
| 581 return TRUE; | |
| 582 } | |
| 583 | |
| 584 //static UMTX debugMutex = NULL; | |
| 585 //static UMTX gDebugMutex; | |
| 586 | |
| 587 | |
| 588 class FormatThreadTest : public SimpleThread | |
| 589 { | |
| 590 public: | |
| 591 int fNum; | |
| 592 int fTraceInfo; | |
| 593 | |
| 594 LocalPointer<ThreadSafeFormat> fTSF; | |
| 595 | |
| 596 FormatThreadTest() // constructor is NOT multithread safe. | |
| 597 : SimpleThread(), | |
| 598 fNum(0), | |
| 599 fTraceInfo(0), | |
| 600 fTSF(NULL), | |
| 601 fOffset(0) | |
| 602 // the locale to use | |
| 603 { | |
| 604 UErrorCode status = U_ZERO_ERROR; // TODO: rearrange code to allow
checking of status. | |
| 605 fTSF.adoptInstead(new ThreadSafeFormat(status)); | |
| 606 static int32_t fgOffset = 0; | |
| 607 fgOffset += 3; | |
| 608 fOffset = fgOffset; | |
| 609 } | |
| 610 | |
| 611 | |
| 612 virtual void run() | |
| 613 { | |
| 614 fTraceInfo = 1; | |
| 615 LocalPointer<NumberFormat> percentFormatter; | |
| 616 UErrorCode status = U_ZERO_ERROR; | |
| 617 | |
| 618 #if 0 | |
| 619 // debugging code, | |
| 620 for (int i=0; i<4000; i++) { | |
| 621 status = U_ZERO_ERROR; | |
| 622 UDataMemory *data1 = udata_openChoice(0, "res", "en_US", isAcceptabl
e, 0, &status); | |
| 623 UDataMemory *data2 = udata_openChoice(0, "res", "fr", isAcceptable,
0, &status); | |
| 624 udata_close(data1); | |
| 625 udata_close(data2); | |
| 626 if (U_FAILURE(status)) { | |
| 627 error("udata_openChoice failed.\n"); | |
| 628 break; | |
| 629 } | |
| 630 } | |
| 631 return; | |
| 632 #endif | |
| 633 | |
| 634 #if 0 | |
| 635 // debugging code, | |
| 636 int m; | |
| 637 for (m=0; m<4000; m++) { | |
| 638 status = U_ZERO_ERROR; | |
| 639 UResourceBundle *res = NULL; | |
| 640 const char *localeName = NULL; | |
| 641 | |
| 642 Locale loc = Locale::getEnglish(); | |
| 643 | |
| 644 localeName = loc.getName(); | |
| 645 // localeName = "en"; | |
| 646 | |
| 647 // ResourceBundle bund = ResourceBundle(0, loc, status); | |
| 648 //umtx_lock(&gDebugMutex); | |
| 649 res = ures_open(NULL, localeName, &status); | |
| 650 //umtx_unlock(&gDebugMutex); | |
| 651 | |
| 652 //umtx_lock(&gDebugMutex); | |
| 653 ures_close(res); | |
| 654 //umtx_unlock(&gDebugMutex); | |
| 655 | |
| 656 if (U_FAILURE(status)) { | |
| 657 error("Resource bundle construction failed.\n"); | |
| 658 break; | |
| 659 } | |
| 660 } | |
| 661 return; | |
| 662 #endif | |
| 663 | |
| 664 // Keep this data here to avoid static initialization. | |
| 665 FormatThreadTestData kNumberFormatTestData[] = | |
| 666 { | |
| 667 FormatThreadTestData((double)5.0, UnicodeString("5", "")), | |
| 668 FormatThreadTestData( 6.0, UnicodeString("6", "")), | |
| 669 FormatThreadTestData( 20.0, UnicodeString("20", "")), | |
| 670 FormatThreadTestData( 8.0, UnicodeString("8", "")), | |
| 671 FormatThreadTestData( 8.3, UnicodeString("8.3", "")), | |
| 672 FormatThreadTestData( 12345, UnicodeString("12,345", "")), | |
| 673 FormatThreadTestData( 81890.23, UnicodeString("81,890.23", "")), | |
| 674 }; | |
| 675 int32_t kNumberFormatTestDataLength = UPRV_LENGTHOF(kNumberFormatTestDat
a); | |
| 676 | |
| 677 // Keep this data here to avoid static initialization. | |
| 678 FormatThreadTestData kPercentFormatTestData[] = | |
| 679 { | |
| 680 FormatThreadTestData((double)5.0, CharsToUnicodeString("500\\u00a0%"
)), | |
| 681 FormatThreadTestData( 1.0, CharsToUnicodeString("100\\u00a0%")), | |
| 682 FormatThreadTestData( 0.26, CharsToUnicodeString("26\\u00a0%")), | |
| 683 FormatThreadTestData( | |
| 684 16384.99, CharsToUnicodeString("1\\u00a0638\\u00a0499\\u00a0%
")), // U+00a0 = NBSP | |
| 685 FormatThreadTestData( | |
| 686 81890.23, CharsToUnicodeString("8\\u00a0189\\u00a0023\\u00a0
%")), | |
| 687 }; | |
| 688 int32_t kPercentFormatTestDataLength = UPRV_LENGTHOF(kPercentFormatTestD
ata); | |
| 689 int32_t iteration; | |
| 690 | |
| 691 status = U_ZERO_ERROR; | |
| 692 LocalPointer<NumberFormat> formatter(NumberFormat::createInstance(Locale
::getEnglish(),status)); | |
| 693 if(U_FAILURE(status)) { | |
| 694 IntlTest::gTest->dataerrln("%s:%d Error %s on NumberFormat::createIn
stance().", | |
| 695 __FILE__, __LINE__, u_errorName(status)); | |
| 696 goto cleanupAndReturn; | |
| 697 } | |
| 698 | |
| 699 percentFormatter.adoptInstead(NumberFormat::createPercentInstance(Locale
::getFrench(),status)); | |
| 700 if(U_FAILURE(status)) { | |
| 701 IntlTest::gTest->errln("%s:%d Error %s on NumberFormat::createPercen
tInstance().", | |
| 702 __FILE__, __LINE__, u_errorName(status)); | |
| 703 goto cleanupAndReturn; | |
| 704 } | |
| 705 | |
| 706 for(iteration = 0;!IntlTest::gTest->getErrors() && iteration<kFormatThre
adIterations;iteration++) | |
| 707 { | |
| 708 | |
| 709 int32_t whichLine = (iteration + fOffset)%kNumberFormatTestDataLengt
h; | |
| 710 | |
| 711 UnicodeString output; | |
| 712 | |
| 713 formatter->format(kNumberFormatTestData[whichLine].number, output); | |
| 714 | |
| 715 if(0 != output.compare(kNumberFormatTestData[whichLine].string)) { | |
| 716 IntlTest::gTest->errln("format().. expected " + kNumberFormatTes
tData[whichLine].string | |
| 717 + " got " + output); | |
| 718 goto cleanupAndReturn; | |
| 719 } | |
| 720 | |
| 721 // Now check percent. | |
| 722 output.remove(); | |
| 723 whichLine = (iteration + fOffset)%kPercentFormatTestDataLength; | |
| 724 | |
| 725 percentFormatter->format(kPercentFormatTestData[whichLine].number, o
utput); | |
| 726 if(0 != output.compare(kPercentFormatTestData[whichLine].string)) | |
| 727 { | |
| 728 IntlTest::gTest->errln("percent format().. \n" + | |
| 729 showDifference(kPercentFormatTestData[whichLine].string,
output)); | |
| 730 goto cleanupAndReturn; | |
| 731 } | |
| 732 | |
| 733 // Test message error | |
| 734 const int kNumberOfMessageTests = 3; | |
| 735 UErrorCode statusToCheck; | |
| 736 UnicodeString patternToCheck; | |
| 737 Locale messageLocale; | |
| 738 Locale countryToCheck; | |
| 739 double currencyToCheck; | |
| 740 | |
| 741 UnicodeString expected; | |
| 742 | |
| 743 // load the cases. | |
| 744 switch((iteration+fOffset) % kNumberOfMessageTests) | |
| 745 { | |
| 746 default: | |
| 747 case 0: | |
| 748 statusToCheck= U_FILE_ACCESS_ERROR; | |
| 749 patternToCheck= "0:Someone from {2} is receiving a #{0}" | |
| 750 " error - {1}. Their telephone call is co
sting " | |
| 751 "{3,number,currency}."; // number,currenc
y | |
| 752 messageLocale= Locale("en","US"); | |
| 753 countryToCheck= Locale("","HR"); | |
| 754 currencyToCheck= 8192.77; | |
| 755 expected= "0:Someone from Croatia is receiving a #4 error - " | |
| 756 "U_FILE_ACCESS_ERROR. Their telephone call is costin
g $8,192.77."; | |
| 757 break; | |
| 758 case 1: | |
| 759 statusToCheck= U_INDEX_OUTOFBOUNDS_ERROR; | |
| 760 patternToCheck= "1:A customer in {2} is rece
iving a #{0} error - {1}. " | |
| 761 "Their telephone call is cos
ting {3,number,currency}."; // number,currency | |
| 762 messageLocale= Locale("de","DE@currency=DEM
"); | |
| 763 countryToCheck= Locale("","BF"); | |
| 764 currencyToCheck= 2.32; | |
| 765 expected= CharsToUnicodeString( | |
| 766 "1:A customer in Burkina Fas
o is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. " | |
| 767 "Their telephone call is cos
ting 2,32\\u00A0DM."); | |
| 768 break; | |
| 769 case 2: | |
| 770 statusToCheck= U_MEMORY_ALLOCATION_ERROR; | |
| 771 patternToCheck= "2:user in {2} is receiving a #{0} error - {1}
. " | |
| 772 "They insist they just spent {3,number,currenc
y} " | |
| 773 "on memory."; // number,currency | |
| 774 messageLocale= Locale("de","AT@currency=ATS
"); // Austrian German | |
| 775 countryToCheck= Locale("","US"); // hmm | |
| 776 currencyToCheck= 40193.12; | |
| 777 expected= CharsToUnicodeString( | |
| 778 "2:user in Vereinigte Staaten is receiving a #7 erro
r" | |
| 779 " - U_MEMORY_ALLOCATION_ERROR. They insist they just
spent" | |
| 780 " \\u00f6S\\u00A040\\u00A0193,12 on memory."); | |
| 781 break; | |
| 782 } | |
| 783 | |
| 784 UnicodeString result; | |
| 785 UErrorCode status = U_ZERO_ERROR; | |
| 786 formatErrorMessage(status,patternToCheck,messageLocale,statusToCheck
, | |
| 787 countryToCheck,currencyToCheck,result); | |
| 788 if(U_FAILURE(status)) | |
| 789 { | |
| 790 UnicodeString tmp(u_errorName(status)); | |
| 791 IntlTest::gTest->errln("Failure on message format, pattern=" + p
atternToCheck + | |
| 792 ", error = " + tmp); | |
| 793 goto cleanupAndReturn; | |
| 794 } | |
| 795 | |
| 796 if(result != expected) | |
| 797 { | |
| 798 IntlTest::gTest->errln("PatternFormat: \n" + showDifference(expe
cted,result)); | |
| 799 goto cleanupAndReturn; | |
| 800 } | |
| 801 // test the Thread Safe Format | |
| 802 UnicodeString appendErr; | |
| 803 if(!fTSF->doStuff(fNum, appendErr, status)) { | |
| 804 IntlTest::gTest->errln(appendErr); | |
| 805 goto cleanupAndReturn; | |
| 806 } | |
| 807 } /* end of for loop */ | |
| 808 | |
| 809 | |
| 810 | |
| 811 cleanupAndReturn: | |
| 812 fTraceInfo = 2; | |
| 813 } | |
| 814 | |
| 815 private: | |
| 816 int32_t fOffset; // where we are testing from. | |
| 817 }; | |
| 818 | |
| 819 // ** The actual test function. | |
| 820 | |
| 821 void MultithreadTest::TestThreadedIntl() | |
| 822 { | |
| 823 UnicodeString theErr; | |
| 824 | |
| 825 UErrorCode threadSafeErr = U_ZERO_ERROR; | |
| 826 | |
| 827 ThreadSafeFormatSharedData sharedData(threadSafeErr); | |
| 828 assertSuccess("initializing ThreadSafeFormat", threadSafeErr, TRUE); | |
| 829 | |
| 830 // | |
| 831 // Create and start the test threads | |
| 832 // | |
| 833 logln("Spawning: %d threads * %d iterations each.", | |
| 834 kFormatThreadThreads, kFormatThreadIterations); | |
| 835 FormatThreadTest tests[kFormatThreadThreads]; | |
| 836 int32_t j; | |
| 837 for(j = 0; j < UPRV_LENGTHOF(tests); j++) { | |
| 838 tests[j].fNum = j; | |
| 839 int32_t threadStatus = tests[j].start(); | |
| 840 if (threadStatus != 0) { | |
| 841 errln("%s:%d System Error %d starting thread number %d.", | |
| 842 __FILE__, __LINE__, threadStatus, j); | |
| 843 return; | |
| 844 } | |
| 845 } | |
| 846 | |
| 847 | |
| 848 for (j=0; j<UPRV_LENGTHOF(tests); j++) { | |
| 849 tests[j].join(); | |
| 850 logln("Thread # %d is complete..", j); | |
| 851 } | |
| 852 } | |
| 853 #endif /* #if !UCONFIG_NO_FORMATTING */ | |
| 854 | |
| 855 | |
| 856 | |
| 857 | |
| 858 | |
| 859 //------------------------------------------------------------------------------
------------- | |
| 860 // | |
| 861 // Collation threading test | |
| 862 // | |
| 863 //------------------------------------------------------------------------------
------------- | |
| 864 #if !UCONFIG_NO_COLLATION | |
| 865 | |
| 866 #define kCollatorThreadThreads 10 // # of threads to spawn | |
| 867 #define kCollatorThreadPatience kCollatorThreadThreads*30 | |
| 868 | |
| 869 struct Line { | |
| 870 UChar buff[25]; | |
| 871 int32_t buflen; | |
| 872 } ; | |
| 873 | |
| 874 static UBool | |
| 875 skipLineBecauseOfBug(const UChar *s, int32_t length) { | |
| 876 // TODO: Fix ICU ticket #8052 | |
| 877 if(length >= 3 && | |
| 878 (s[0] == 0xfb2 || s[0] == 0xfb3) && | |
| 879 s[1] == 0x334 && | |
| 880 (s[2] == 0xf73 || s[2] == 0xf75 || s[2] == 0xf81)) { | |
| 881 return TRUE; | |
| 882 } | |
| 883 return FALSE; | |
| 884 } | |
| 885 | |
| 886 static UCollationResult | |
| 887 normalizeResult(int32_t result) { | |
| 888 return result<0 ? UCOL_LESS : result==0 ? UCOL_EQUAL : UCOL_GREATER; | |
| 889 } | |
| 890 | |
| 891 class CollatorThreadTest : public SimpleThread | |
| 892 { | |
| 893 private: | |
| 894 const Collator *coll; | |
| 895 const Line *lines; | |
| 896 int32_t noLines; | |
| 897 UBool isAtLeastUCA62; | |
| 898 public: | |
| 899 CollatorThreadTest() : SimpleThread(), | |
| 900 coll(NULL), | |
| 901 lines(NULL), | |
| 902 noLines(0), | |
| 903 isAtLeastUCA62(TRUE) | |
| 904 { | |
| 905 }; | |
| 906 void setCollator(Collator *c, Line *l, int32_t nl, UBool atLeastUCA62) | |
| 907 { | |
| 908 coll = c; | |
| 909 lines = l; | |
| 910 noLines = nl; | |
| 911 isAtLeastUCA62 = atLeastUCA62; | |
| 912 } | |
| 913 virtual void run() { | |
| 914 uint8_t sk1[1024], sk2[1024]; | |
| 915 uint8_t *oldSk = NULL, *newSk = sk1; | |
| 916 int32_t oldLen = 0; | |
| 917 int32_t prev = 0; | |
| 918 int32_t i = 0; | |
| 919 | |
| 920 for(i = 0; i < noLines; i++) { | |
| 921 if(lines[i].buflen == 0) { continue; } | |
| 922 | |
| 923 if(skipLineBecauseOfBug(lines[i].buff, lines[i].buflen)) { continue;
} | |
| 924 | |
| 925 int32_t resLen = coll->getSortKey(lines[i].buff, lines[i].buflen, ne
wSk, 1024); | |
| 926 | |
| 927 if(oldSk != NULL) { | |
| 928 int32_t skres = strcmp((char *)oldSk, (char *)newSk); | |
| 929 int32_t cmpres = coll->compare(lines[prev].buff, lines[prev].buf
len, lines[i].buff, lines[i].buflen); | |
| 930 int32_t cmpres2 = coll->compare(lines[i].buff, lines[i].buflen,
lines[prev].buff, lines[prev].buflen); | |
| 931 | |
| 932 if(cmpres != -cmpres2) { | |
| 933 IntlTest::gTest->errln(UnicodeString("Compare result not sym
metrical on line ") + (i + 1)); | |
| 934 break; | |
| 935 } | |
| 936 | |
| 937 if(cmpres != normalizeResult(skres)) { | |
| 938 IntlTest::gTest->errln(UnicodeString("Difference between col
l->compare and sortkey compare on line ") + (i + 1)); | |
| 939 break; | |
| 940 } | |
| 941 | |
| 942 int32_t res = cmpres; | |
| 943 if(res == 0 && !isAtLeastUCA62) { | |
| 944 // Up to UCA 6.1, the collation test files use a custom tie-
breaker, | |
| 945 // comparing the raw input strings. | |
| 946 res = u_strcmpCodePointOrder(lines[prev].buff, lines[i].buff
); | |
| 947 // Starting with UCA 6.2, the collation test files use the s
tandard UCA tie-breaker, | |
| 948 // comparing the NFD versions of the input strings, | |
| 949 // which we do via setting strength=identical. | |
| 950 } | |
| 951 if(res > 0) { | |
| 952 IntlTest::gTest->errln(UnicodeString("Line is not greater or
equal than previous line, for line ") + (i + 1)); | |
| 953 break; | |
| 954 } | |
| 955 } | |
| 956 | |
| 957 oldSk = newSk; | |
| 958 oldLen = resLen; | |
| 959 (void)oldLen; // Suppress set but not used warning. | |
| 960 prev = i; | |
| 961 | |
| 962 newSk = (newSk == sk1)?sk2:sk1; | |
| 963 } | |
| 964 } | |
| 965 }; | |
| 966 | |
| 967 void MultithreadTest::TestCollators() | |
| 968 { | |
| 969 | |
| 970 UErrorCode status = U_ZERO_ERROR; | |
| 971 FILE *testFile = NULL; | |
| 972 char testDataPath[1024]; | |
| 973 strcpy(testDataPath, IntlTest::getSourceTestData(status)); | |
| 974 if (U_FAILURE(status)) { | |
| 975 errln("ERROR: could not open test data %s", u_errorName(status)); | |
| 976 return; | |
| 977 } | |
| 978 strcat(testDataPath, "CollationTest_"); | |
| 979 | |
| 980 const char* type = "NON_IGNORABLE"; | |
| 981 | |
| 982 const char *ext = ".txt"; | |
| 983 if(testFile) { | |
| 984 fclose(testFile); | |
| 985 } | |
| 986 char buffer[1024]; | |
| 987 strcpy(buffer, testDataPath); | |
| 988 strcat(buffer, type); | |
| 989 size_t bufLen = strlen(buffer); | |
| 990 | |
| 991 // we try to open 3 files: | |
| 992 // path/CollationTest_type.txt | |
| 993 // path/CollationTest_type_SHORT.txt | |
| 994 // path/CollationTest_type_STUB.txt | |
| 995 // we are going to test with the first one that we manage to open. | |
| 996 | |
| 997 strcpy(buffer+bufLen, ext); | |
| 998 | |
| 999 testFile = fopen(buffer, "rb"); | |
| 1000 | |
| 1001 if(testFile == 0) { | |
| 1002 strcpy(buffer+bufLen, "_SHORT"); | |
| 1003 strcat(buffer, ext); | |
| 1004 testFile = fopen(buffer, "rb"); | |
| 1005 | |
| 1006 if(testFile == 0) { | |
| 1007 strcpy(buffer+bufLen, "_STUB"); | |
| 1008 strcat(buffer, ext); | |
| 1009 testFile = fopen(buffer, "rb"); | |
| 1010 | |
| 1011 if (testFile == 0) { | |
| 1012 *(buffer+bufLen) = 0; | |
| 1013 dataerrln("could not open any of the conformance test files, tri
ed opening base %s", buffer); | |
| 1014 return; | |
| 1015 } else { | |
| 1016 infoln( | |
| 1017 "INFO: Working with the stub file.\n" | |
| 1018 "If you need the full conformance test, please\n" | |
| 1019 "download the appropriate data files from:\n" | |
| 1020 "http://source.icu-project.org/repos/icu/tools/trunk/unicode
tools/com/ibm/text/data/"); | |
| 1021 } | |
| 1022 } | |
| 1023 } | |
| 1024 | |
| 1025 LocalArray<Line> lines(new Line[200000]); | |
| 1026 memset(lines.getAlias(), 0, sizeof(Line)*200000); | |
| 1027 int32_t lineNum = 0; | |
| 1028 | |
| 1029 UChar bufferU[1024]; | |
| 1030 uint32_t first = 0; | |
| 1031 | |
| 1032 while (fgets(buffer, 1024, testFile) != NULL) { | |
| 1033 if(*buffer == 0 || buffer[0] == '#') { | |
| 1034 // Store empty and comment lines so that errors are reported | |
| 1035 // for the real test file lines. | |
| 1036 lines[lineNum].buflen = 0; | |
| 1037 lines[lineNum].buff[0] = 0; | |
| 1038 } else { | |
| 1039 int32_t buflen = u_parseString(buffer, bufferU, 1024, &first, &statu
s); | |
| 1040 lines[lineNum].buflen = buflen; | |
| 1041 u_memcpy(lines[lineNum].buff, bufferU, buflen); | |
| 1042 lines[lineNum].buff[buflen] = 0; | |
| 1043 } | |
| 1044 lineNum++; | |
| 1045 } | |
| 1046 fclose(testFile); | |
| 1047 if(U_FAILURE(status)) { | |
| 1048 dataerrln("Couldn't read the test file!"); | |
| 1049 return; | |
| 1050 } | |
| 1051 | |
| 1052 UVersionInfo uniVersion; | |
| 1053 static const UVersionInfo v62 = { 6, 2, 0, 0 }; | |
| 1054 u_getUnicodeVersion(uniVersion); | |
| 1055 UBool isAtLeastUCA62 = uprv_memcmp(uniVersion, v62, 4) >= 0; | |
| 1056 | |
| 1057 LocalPointer<Collator> coll(Collator::createInstance(Locale::getRoot(), stat
us)); | |
| 1058 if(U_FAILURE(status)) { | |
| 1059 errcheckln(status, "Couldn't open UCA collator"); | |
| 1060 return; | |
| 1061 } | |
| 1062 coll->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status); | |
| 1063 coll->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status); | |
| 1064 coll->setAttribute(UCOL_CASE_LEVEL, UCOL_OFF, status); | |
| 1065 coll->setAttribute(UCOL_STRENGTH, isAtLeastUCA62 ? UCOL_IDENTICAL : UCOL_TER
TIARY, status); | |
| 1066 coll->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE, status); | |
| 1067 | |
| 1068 int32_t spawnResult = 0; | |
| 1069 LocalArray<CollatorThreadTest> tests(new CollatorThreadTest[kCollatorThreadT
hreads]); | |
| 1070 | |
| 1071 logln(UnicodeString("Spawning: ") + kCollatorThreadThreads + " threads * " +
kFormatThreadIterations + " iterations each."); | |
| 1072 int32_t j = 0; | |
| 1073 for(j = 0; j < kCollatorThreadThreads; j++) { | |
| 1074 //logln("Setting collator %i", j); | |
| 1075 tests[j].setCollator(coll.getAlias(), lines.getAlias(), lineNum, isAtLea
stUCA62); | |
| 1076 } | |
| 1077 for(j = 0; j < kCollatorThreadThreads; j++) { | |
| 1078 log("%i ", j); | |
| 1079 spawnResult = tests[j].start(); | |
| 1080 if(spawnResult != 0) { | |
| 1081 errln("%s:%d THREAD INFO: thread %d failed to start with status %d",
__FILE__, __LINE__, j, spawnResult); | |
| 1082 return; | |
| 1083 } | |
| 1084 } | |
| 1085 logln("Spawned all"); | |
| 1086 | |
| 1087 for(int32_t i=0;i<kCollatorThreadThreads;i++) { | |
| 1088 tests[i].join(); | |
| 1089 //logln(UnicodeString("Test #") + i + " is complete.. "); | |
| 1090 } | |
| 1091 } | |
| 1092 | |
| 1093 #endif /* #if !UCONFIG_NO_COLLATION */ | |
| 1094 | |
| 1095 | |
| 1096 | |
| 1097 | |
| 1098 //------------------------------------------------------------------------------
------------- | |
| 1099 // | |
| 1100 // StringThreadTest2 | |
| 1101 // | |
| 1102 //------------------------------------------------------------------------------
------------- | |
| 1103 | |
| 1104 const int kStringThreadIterations = 2500;// # of iterations per thread | |
| 1105 const int kStringThreadThreads = 10; // # of threads to spawn | |
| 1106 | |
| 1107 | |
| 1108 class StringThreadTest2 : public SimpleThread | |
| 1109 { | |
| 1110 public: | |
| 1111 int fNum; | |
| 1112 int fTraceInfo; | |
| 1113 static const UnicodeString *gSharedString; | |
| 1114 | |
| 1115 StringThreadTest2() // constructor is NOT multithread safe. | |
| 1116 : SimpleThread(), | |
| 1117 fTraceInfo(0) | |
| 1118 { | |
| 1119 }; | |
| 1120 | |
| 1121 | |
| 1122 virtual void run() | |
| 1123 { | |
| 1124 fTraceInfo = 1; | |
| 1125 int loopCount = 0; | |
| 1126 | |
| 1127 for (loopCount = 0; loopCount < kStringThreadIterations; loopCount++) { | |
| 1128 if (*gSharedString != "This is the original test string.") { | |
| 1129 IntlTest::gTest->errln("%s:%d Original string is corrupt.", __FI
LE__, __LINE__); | |
| 1130 break; | |
| 1131 } | |
| 1132 UnicodeString s1 = *gSharedString; | |
| 1133 s1 += "cat this"; | |
| 1134 UnicodeString s2(s1); | |
| 1135 UnicodeString s3 = *gSharedString; | |
| 1136 s2 = s3; | |
| 1137 s3.truncate(12); | |
| 1138 s2.truncate(0); | |
| 1139 } | |
| 1140 | |
| 1141 fTraceInfo = 2; | |
| 1142 } | |
| 1143 | |
| 1144 }; | |
| 1145 | |
| 1146 const UnicodeString *StringThreadTest2::gSharedString = NULL; | |
| 1147 | |
| 1148 // ** The actual test function. | |
| 1149 | |
| 1150 | |
| 1151 void MultithreadTest::TestString() | |
| 1152 { | |
| 1153 int j; | |
| 1154 StringThreadTest2::gSharedString = new UnicodeString("This is the original t
est string."); | |
| 1155 StringThreadTest2 tests[kStringThreadThreads]; | |
| 1156 | |
| 1157 logln(UnicodeString("Spawning: ") + kStringThreadThreads + " threads * " + k
StringThreadIterations + " iterations each."); | |
| 1158 for(j = 0; j < kStringThreadThreads; j++) { | |
| 1159 int32_t threadStatus = tests[j].start(); | |
| 1160 if (threadStatus != 0) { | |
| 1161 errln("%s:%d System Error %d starting thread number %d.", __FILE__,
__LINE__, threadStatus, j); | |
| 1162 } | |
| 1163 } | |
| 1164 | |
| 1165 // Force a failure, to verify test is functioning and can report errors. | |
| 1166 // const_cast<UnicodeString *>(StringThreadTest2::gSharedString)->setCharAt(
5, 'x'); | |
| 1167 | |
| 1168 for(j=0; j<kStringThreadThreads; j++) { | |
| 1169 tests[j].join(); | |
| 1170 logln(UnicodeString("Test #") + j + " is complete.. "); | |
| 1171 } | |
| 1172 | |
| 1173 delete StringThreadTest2::gSharedString; | |
| 1174 StringThreadTest2::gSharedString = NULL; | |
| 1175 } | |
| 1176 | |
| 1177 | |
| 1178 // | |
| 1179 // Test for ticket #10673, race in cache code in AnyTransliterator. | |
| 1180 // It's difficult to make the original unsafe code actually fail, but | |
| 1181 // this test will fairly reliably take the code path for races in | |
| 1182 // populating the cache. | |
| 1183 // | |
| 1184 | |
| 1185 #if !UCONFIG_NO_TRANSLITERATION | |
| 1186 Transliterator *gSharedTranslit = NULL; | |
| 1187 class TxThread: public SimpleThread { | |
| 1188 public: | |
| 1189 TxThread() {}; | |
| 1190 ~TxThread(); | |
| 1191 void run(); | |
| 1192 }; | |
| 1193 | |
| 1194 TxThread::~TxThread() {} | |
| 1195 void TxThread::run() { | |
| 1196 UnicodeString greekString("\\u03B4\\u03B9\\u03B1\\u03C6\\u03BF\\u03C1\\u03B5
\\u03C4\\u03B9\\u03BA\\u03BF\\u03CD\\u03C2"); | |
| 1197 greekString = greekString.unescape(); | |
| 1198 gSharedTranslit->transliterate(greekString); | |
| 1199 if (greekString[0] != 0x64) // 'd'. The whole transliterated string is
"diaphoretikous" (accented u). | |
| 1200 { | |
| 1201 IntlTest::gTest->errln("%s:%d Transliteration failed.", __FILE__, __LINE
__); | |
| 1202 } | |
| 1203 } | |
| 1204 #endif | |
| 1205 | |
| 1206 | |
| 1207 void MultithreadTest::TestAnyTranslit() { | |
| 1208 #if !UCONFIG_NO_TRANSLITERATION | |
| 1209 UErrorCode status = U_ZERO_ERROR; | |
| 1210 LocalPointer<Transliterator> tx(Transliterator::createInstance("Any-Latin",
UTRANS_FORWARD, status)); | |
| 1211 if (U_FAILURE(status)) { | |
| 1212 dataerrln("File %s, Line %d: Error, status = %s", __FILE__, __LINE__, u_
errorName(status)); | |
| 1213 return; | |
| 1214 } | |
| 1215 gSharedTranslit = tx.getAlias(); | |
| 1216 TxThread threads[4]; | |
| 1217 int32_t i; | |
| 1218 for (i=0; i<UPRV_LENGTHOF(threads); i++) { | |
| 1219 threads[i].start(); | |
| 1220 } | |
| 1221 | |
| 1222 for (i=0; i<UPRV_LENGTHOF(threads); i++) { | |
| 1223 threads[i].join(); | |
| 1224 } | |
| 1225 gSharedTranslit = NULL; | |
| 1226 #endif // !UCONFIG_NO_TRANSLITERATION | |
| 1227 } | |
| 1228 | |
| 1229 | |
| 1230 // | |
| 1231 // Condition Variables Test | |
| 1232 // Create a swarm of threads. | |
| 1233 // Using a mutex and a condition variables each thread | |
| 1234 // Increments a global count of started threads. | |
| 1235 // Broadcasts that it has started. | |
| 1236 // Waits on the condition that all threads have started. | |
| 1237 // Increments a global count of finished threads. | |
| 1238 // Waits on the condition that all threads have finished. | |
| 1239 // Exits. | |
| 1240 // | |
| 1241 | |
| 1242 class CondThread: public SimpleThread { | |
| 1243 public: | |
| 1244 CondThread() :fFinished(false) {}; | |
| 1245 ~CondThread() {}; | |
| 1246 void run(); | |
| 1247 bool fFinished; | |
| 1248 }; | |
| 1249 | |
| 1250 static UMutex gCTMutex = U_MUTEX_INITIALIZER; | |
| 1251 static UConditionVar gCTConditionVar = U_CONDITION_INITIALIZER; | |
| 1252 int gConditionTestOne = 1; // Value one. Non-const, extern linkage to inhibit | |
| 1253 // compiler assuming a known value. | |
| 1254 int gStartedThreads; | |
| 1255 int gFinishedThreads; | |
| 1256 static const int NUMTHREADS = 10; | |
| 1257 | |
| 1258 | |
| 1259 // Worker thread function. | |
| 1260 void CondThread::run() { | |
| 1261 umtx_lock(&gCTMutex); | |
| 1262 gStartedThreads += gConditionTestOne; | |
| 1263 umtx_condBroadcast(&gCTConditionVar); | |
| 1264 | |
| 1265 while (gStartedThreads < NUMTHREADS) { | |
| 1266 if (gFinishedThreads != 0) { | |
| 1267 IntlTest::gTest->errln("File %s, Line %d: Error, gStartedThreads = %
d, gFinishedThreads = %d", | |
| 1268 __FILE__, __LINE__, gStartedThreads, gFinishedThrea
ds); | |
| 1269 } | |
| 1270 umtx_condWait(&gCTConditionVar, &gCTMutex); | |
| 1271 } | |
| 1272 | |
| 1273 gFinishedThreads += gConditionTestOne; | |
| 1274 fFinished = true; | |
| 1275 umtx_condBroadcast(&gCTConditionVar); | |
| 1276 | |
| 1277 while (gFinishedThreads < NUMTHREADS) { | |
| 1278 umtx_condWait(&gCTConditionVar, &gCTMutex); | |
| 1279 } | |
| 1280 umtx_unlock(&gCTMutex); | |
| 1281 } | |
| 1282 | |
| 1283 void MultithreadTest::TestConditionVariables() { | |
| 1284 gStartedThreads = 0; | |
| 1285 gFinishedThreads = 0; | |
| 1286 int i; | |
| 1287 | |
| 1288 umtx_lock(&gCTMutex); | |
| 1289 CondThread *threads[NUMTHREADS]; | |
| 1290 for (i=0; i<NUMTHREADS; ++i) { | |
| 1291 threads[i] = new CondThread; | |
| 1292 threads[i]->start(); | |
| 1293 } | |
| 1294 | |
| 1295 while (gStartedThreads < NUMTHREADS) { | |
| 1296 umtx_condWait(&gCTConditionVar, &gCTMutex); | |
| 1297 } | |
| 1298 | |
| 1299 while (gFinishedThreads < NUMTHREADS) { | |
| 1300 umtx_condWait(&gCTConditionVar, &gCTMutex); | |
| 1301 } | |
| 1302 | |
| 1303 umtx_unlock(&gCTMutex); | |
| 1304 | |
| 1305 for (i=0; i<NUMTHREADS; ++i) { | |
| 1306 if (!threads[i]->fFinished) { | |
| 1307 errln("File %s, Line %d: Error, threads[%d]->fFinished == false", __
FILE__, __LINE__, i); | |
| 1308 } | |
| 1309 } | |
| 1310 | |
| 1311 for (i=0; i<NUMTHREADS; ++i) { | |
| 1312 threads[i]->join(); | |
| 1313 delete threads[i]; | |
| 1314 } | |
| 1315 } | |
| 1316 | |
| 1317 | |
| 1318 // | |
| 1319 // Unified Cache Test | |
| 1320 // | |
| 1321 | |
| 1322 // Each thread fetches a pair of objects. There are 8 distinct pairs: | |
| 1323 // ("en_US", "bs"), ("en_GB", "ca"), ("fr_FR", "ca_AD") etc. | |
| 1324 // These pairs represent 8 distinct languages | |
| 1325 | |
| 1326 // Note that only one value per language gets created in the cache. | |
| 1327 // In particular each cached value can have multiple keys. | |
| 1328 static const char *gCacheLocales[] = { | |
| 1329 "en_US", "en_GB", "fr_FR", "fr", | |
| 1330 "de", "sr_ME", "sr_BA", "sr_CS"}; | |
| 1331 static const char *gCacheLocales2[] = { | |
| 1332 "bs", "ca", "ca_AD", "ca_ES", | |
| 1333 "en_US", "fi", "ff_CM", "ff_GN"}; | |
| 1334 | |
| 1335 static int32_t gObjectsCreated = 0; // protected by gCTMutex | |
| 1336 static const int32_t CACHE_LOAD = 3; | |
| 1337 | |
| 1338 class UCTMultiThreadItem : public SharedObject { | |
| 1339 public: | |
| 1340 char *value; | |
| 1341 UCTMultiThreadItem(const char *x) : value(NULL) { | |
| 1342 value = uprv_strdup(x); | |
| 1343 } | |
| 1344 virtual ~UCTMultiThreadItem() { | |
| 1345 uprv_free(value); | |
| 1346 } | |
| 1347 }; | |
| 1348 | |
| 1349 U_NAMESPACE_BEGIN | |
| 1350 | |
| 1351 template<> U_EXPORT | |
| 1352 const UCTMultiThreadItem *LocaleCacheKey<UCTMultiThreadItem>::createObject( | |
| 1353 const void *context, UErrorCode &status) const { | |
| 1354 const UnifiedCache *cacheContext = (const UnifiedCache *) context; | |
| 1355 | |
| 1356 if (uprv_strcmp(fLoc.getLanguage(), fLoc.getName()) != 0) { | |
| 1357 const UCTMultiThreadItem *result = NULL; | |
| 1358 if (cacheContext == NULL) { | |
| 1359 UnifiedCache::getByLocale(fLoc.getLanguage(), result, status); | |
| 1360 return result; | |
| 1361 } | |
| 1362 cacheContext->get(LocaleCacheKey<UCTMultiThreadItem>(fLoc.getLanguage())
, result, status); | |
| 1363 return result; | |
| 1364 } | |
| 1365 | |
| 1366 umtx_lock(&gCTMutex); | |
| 1367 bool firstObject = (gObjectsCreated == 0); | |
| 1368 if (firstObject) { | |
| 1369 // Force the first object creation that comes through to wait | |
| 1370 // until other have completed. Verifies that cache doesn't | |
| 1371 // deadlock when a creation is slow. | |
| 1372 | |
| 1373 // Note that gObjectsCreated needs to be incremeneted from 0 to 1 | |
| 1374 // early, to keep subsequent threads from entering this path. | |
| 1375 gObjectsCreated = 1; | |
| 1376 while (gObjectsCreated < 3) { | |
| 1377 umtx_condWait(&gCTConditionVar, &gCTMutex); | |
| 1378 } | |
| 1379 } | |
| 1380 umtx_unlock(&gCTMutex); | |
| 1381 | |
| 1382 const UCTMultiThreadItem *result = | |
| 1383 new UCTMultiThreadItem(fLoc.getLanguage()); | |
| 1384 if (result == NULL) { | |
| 1385 status = U_MEMORY_ALLOCATION_ERROR; | |
| 1386 } else { | |
| 1387 result->addRef(); | |
| 1388 } | |
| 1389 | |
| 1390 // Log that we created an object. The first object was already counted, | |
| 1391 // don't do it again. | |
| 1392 umtx_lock(&gCTMutex); | |
| 1393 if (!firstObject) { | |
| 1394 gObjectsCreated += 1; | |
| 1395 } | |
| 1396 umtx_condBroadcast(&gCTConditionVar); | |
| 1397 umtx_unlock(&gCTMutex); | |
| 1398 | |
| 1399 return result; | |
| 1400 } | |
| 1401 | |
| 1402 U_NAMESPACE_END | |
| 1403 | |
| 1404 class UnifiedCacheThread: public SimpleThread { | |
| 1405 public: | |
| 1406 UnifiedCacheThread( | |
| 1407 const UnifiedCache *cache, | |
| 1408 const char *loc, | |
| 1409 const char *loc2) : fCache(cache), fLoc(loc), fLoc2(loc2) {}; | |
| 1410 ~UnifiedCacheThread() {}; | |
| 1411 void run(); | |
| 1412 void exerciseByLocale(const Locale &); | |
| 1413 const UnifiedCache *fCache; | |
| 1414 Locale fLoc; | |
| 1415 Locale fLoc2; | |
| 1416 }; | |
| 1417 | |
| 1418 void UnifiedCacheThread::exerciseByLocale(const Locale &locale) { | |
| 1419 UErrorCode status = U_ZERO_ERROR; | |
| 1420 const UCTMultiThreadItem *origItem = NULL; | |
| 1421 fCache->get( | |
| 1422 LocaleCacheKey<UCTMultiThreadItem>(locale), fCache, origItem, status
); | |
| 1423 U_ASSERT(U_SUCCESS(status)); | |
| 1424 if (uprv_strcmp(locale.getLanguage(), origItem->value)) { | |
| 1425 IntlTest::gTest->errln( | |
| 1426 "%s:%d Expected %s, got %s", __FILE__, __LINE__, | |
| 1427 locale.getLanguage(), | |
| 1428 origItem->value); | |
| 1429 } | |
| 1430 | |
| 1431 // Fetch the same item again many times. We should always get the same | |
| 1432 // pointer since this client is already holding onto it | |
| 1433 for (int32_t i = 0; i < 1000; ++i) { | |
| 1434 const UCTMultiThreadItem *item = NULL; | |
| 1435 fCache->get( | |
| 1436 LocaleCacheKey<UCTMultiThreadItem>(locale), fCache, item, status
); | |
| 1437 if (item != origItem) { | |
| 1438 IntlTest::gTest->errln( | |
| 1439 "%s:%d Expected to get the same pointer", | |
| 1440 __FILE__, | |
| 1441 __LINE__); | |
| 1442 } | |
| 1443 if (item != NULL) { | |
| 1444 item->removeRef(); | |
| 1445 } | |
| 1446 } | |
| 1447 origItem->removeRef(); | |
| 1448 } | |
| 1449 | |
| 1450 void UnifiedCacheThread::run() { | |
| 1451 // Run the exercise with 2 different locales so that we can exercise | |
| 1452 // eviction more. If each thread exerices just one locale, then | |
| 1453 // eviction can't start until the threads end. | |
| 1454 exerciseByLocale(fLoc); | |
| 1455 exerciseByLocale(fLoc2); | |
| 1456 } | |
| 1457 | |
| 1458 void MultithreadTest::TestUnifiedCache() { | |
| 1459 | |
| 1460 // Start with our own local cache so that we have complete control | |
| 1461 // and set the eviction policy to evict starting with 2 unused | |
| 1462 // values | |
| 1463 UErrorCode status = U_ZERO_ERROR; | |
| 1464 UnifiedCache::getInstance(status); | |
| 1465 UnifiedCache cache(status); | |
| 1466 cache.setEvictionPolicy(2, 0, status); | |
| 1467 U_ASSERT(U_SUCCESS(status)); | |
| 1468 | |
| 1469 gFinishedThreads = 0; | |
| 1470 gObjectsCreated = 0; | |
| 1471 | |
| 1472 UnifiedCacheThread *threads[CACHE_LOAD][UPRV_LENGTHOF(gCacheLocales)]; | |
| 1473 for (int32_t i=0; i<CACHE_LOAD; ++i) { | |
| 1474 for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) { | |
| 1475 // Each thread works with a pair of locales. | |
| 1476 threads[i][j] = new UnifiedCacheThread( | |
| 1477 &cache, gCacheLocales[j], gCacheLocales2[j]); | |
| 1478 threads[i][j]->start(); | |
| 1479 } | |
| 1480 } | |
| 1481 | |
| 1482 for (int32_t i=0; i<CACHE_LOAD; ++i) { | |
| 1483 for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) { | |
| 1484 threads[i][j]->join(); | |
| 1485 } | |
| 1486 } | |
| 1487 // Because of cache eviction, we can't assert exactly how many | |
| 1488 // distinct objects get created over the course of this run. | |
| 1489 // However we know that at least 8 objects get created because that | |
| 1490 // is how many distinct languages we have in our test. | |
| 1491 if (gObjectsCreated < 8) { | |
| 1492 errln("%s:%d Too few objects created.", __FILE__, __LINE__); | |
| 1493 } | |
| 1494 // We know that each thread cannot create more than 2 objects in | |
| 1495 // the cache, and there are UPRV_LENGTHOF(gCacheLocales) pairs of | |
| 1496 // objects fetched from the cache. If the threads run in series because | |
| 1497 // of eviction, at worst case each thread creates two objects. | |
| 1498 if (gObjectsCreated > 2 * CACHE_LOAD * UPRV_LENGTHOF(gCacheLocales)) { | |
| 1499 errln("%s:%d Too many objects created, got %d, expected %d", __FILE__, _
_LINE__, gObjectsCreated, 2 * CACHE_LOAD * UPRV_LENGTHOF(gCacheLocales)); | |
| 1500 | |
| 1501 } | |
| 1502 | |
| 1503 assertEquals("unused values", 2, cache.unusedCount()); | |
| 1504 | |
| 1505 // clean up threads | |
| 1506 for (int32_t i=0; i<CACHE_LOAD; ++i) { | |
| 1507 for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) { | |
| 1508 delete threads[i][j]; | |
| 1509 } | |
| 1510 } | |
| 1511 } | |
| 1512 | |
| 1513 #if !UCONFIG_NO_TRANSLITERATION | |
| 1514 // | |
| 1515 // BreakTransliterator Threading Test | |
| 1516 // This is a test for bug #11603. Test verified to fail prior to fix. | |
| 1517 // | |
| 1518 | |
| 1519 static const Transliterator *gSharedTransliterator; | |
| 1520 static const UnicodeString *gTranslitInput; | |
| 1521 static const UnicodeString *gTranslitExpected; | |
| 1522 | |
| 1523 class BreakTranslitThread: public SimpleThread { | |
| 1524 public: | |
| 1525 BreakTranslitThread() {}; | |
| 1526 ~BreakTranslitThread() {}; | |
| 1527 void run(); | |
| 1528 }; | |
| 1529 | |
| 1530 void BreakTranslitThread::run() { | |
| 1531 for (int i=0; i<10; i++) { | |
| 1532 icu::UnicodeString s(*gTranslitInput); | |
| 1533 gSharedTransliterator->transliterate(s); | |
| 1534 if (*gTranslitExpected != s) { | |
| 1535 IntlTest::gTest->errln("%s:%d Transliteration threading failure.", _
_FILE__, __LINE__); | |
| 1536 break; | |
| 1537 } | |
| 1538 } | |
| 1539 } | |
| 1540 | |
| 1541 void MultithreadTest::TestBreakTranslit() { | |
| 1542 UErrorCode status = U_ZERO_ERROR; | |
| 1543 UnicodeString input( | |
| 1544 "\\u0E42\\u0E14\\u0E22\\u0E1E\\u0E37\\u0E49\\u0E19\\u0E10\\u0E32\\u0E19\
\u0E41\\u0E25\\u0E49\\u0E27,"); | |
| 1545 input = input.unescape(); | |
| 1546 gTranslitInput = &input; | |
| 1547 | |
| 1548 gSharedTransliterator = Transliterator::createInstance( | |
| 1549 UNICODE_STRING_SIMPLE("Any-Latin; Lower; NFD; [:Diacritic:]Remove; NFC;
Latin-ASCII;"), UTRANS_FORWARD, status); | |
| 1550 if (!gSharedTransliterator) { | |
| 1551 return; | |
| 1552 } | |
| 1553 TSMTHREAD_ASSERT_SUCCESS(status); | |
| 1554 | |
| 1555 UnicodeString expected(*gTranslitInput); | |
| 1556 gSharedTransliterator->transliterate(expected); | |
| 1557 gTranslitExpected = &expected; | |
| 1558 | |
| 1559 BreakTranslitThread threads[4]; | |
| 1560 for (int i=0; i<UPRV_LENGTHOF(threads); ++i) { | |
| 1561 threads[i].start(); | |
| 1562 } | |
| 1563 for (int i=0; i<UPRV_LENGTHOF(threads); ++i) { | |
| 1564 threads[i].join(); | |
| 1565 } | |
| 1566 | |
| 1567 delete gSharedTransliterator; | |
| 1568 gTranslitInput = NULL; | |
| 1569 gTranslitExpected = NULL; | |
| 1570 } | |
| 1571 | |
| 1572 #endif /* !UCONFIG_NO_TRANSLITERATION */ | |
| OLD | NEW |