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 |