]> git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/tsmthred.cpp
ICU-551.24.tar.gz
[apple/icu.git] / icuSources / test / intltest / tsmthred.cpp
1 /********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 1999-2014, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 ********************************************************************/
6
7 #if defined(hpux)
8 # ifndef _INCLUDE_POSIX_SOURCE
9 # define _INCLUDE_POSIX_SOURCE
10 # endif
11 #endif
12
13 #include "simplethread.h"
14
15 #include "unicode/utypes.h"
16 #include "unicode/ustring.h"
17 #include "umutex.h"
18 #include "cmemory.h"
19 #include "cstring.h"
20 #include "uparse.h"
21 #include "unicode/localpointer.h"
22 #include "unicode/resbund.h"
23 #include "unicode/udata.h"
24 #include "unicode/uloc.h"
25 #include "unicode/locid.h"
26 #include "putilimp.h"
27 #include "intltest.h"
28 #include "tsmthred.h"
29 #include "unicode/ushape.h"
30 #include "unicode/translit.h"
31 #include "sharedobject.h"
32 #include "unifiedcache.h"
33 #include "uassert.h"
34
35 #if U_PLATFORM_USES_ONLY_WIN32_API
36 /* Prefer native Windows APIs even if POSIX is implemented (i.e., on Cygwin). */
37 # undef POSIX
38 #elif U_PLATFORM_IMPLEMENTS_POSIX
39 # define POSIX
40 #else
41 # undef POSIX
42 #endif
43
44 /* Needed by z/OS to get usleep */
45 #if U_PLATFORM == U_PF_OS390
46 #define __DOT1 1
47 #define __UU
48 #ifndef _XPG4_2
49 #define _XPG4_2
50 #endif
51 #include <unistd.h>
52 #endif
53 #if defined(POSIX)
54
55 #define HAVE_IMP
56
57 #if (ICU_USE_THREADS == 1)
58 #include <pthread.h>
59 #endif
60
61 #if defined(__hpux) && defined(HPUX_CMA)
62 # if defined(read) // read being defined as cma_read causes trouble with iostream::read
63 # undef read
64 # endif
65 #endif
66
67 /* Define __EXTENSIONS__ for Solaris and old friends in strict mode. */
68 #ifndef __EXTENSIONS__
69 #define __EXTENSIONS__
70 #endif
71
72 #if U_PLATFORM == U_PF_OS390
73 #include <sys/types.h>
74 #endif
75
76 #if U_PLATFORM != U_PF_OS390
77 #include <signal.h>
78 #endif
79
80 /* Define _XPG4_2 for Solaris and friends. */
81 #ifndef _XPG4_2
82 #define _XPG4_2
83 #endif
84
85 /* Define __USE_XOPEN_EXTENDED for Linux and glibc. */
86 #ifndef __USE_XOPEN_EXTENDED
87 #define __USE_XOPEN_EXTENDED
88 #endif
89
90 /* Define _INCLUDE_XOPEN_SOURCE_EXTENDED for HP/UX (11?). */
91 #ifndef _INCLUDE_XOPEN_SOURCE_EXTENDED
92 #define _INCLUDE_XOPEN_SOURCE_EXTENDED
93 #endif
94
95 #include <unistd.h>
96
97 #endif
98 /* HPUX */
99 #ifdef sleep
100 #undef sleep
101 #endif
102
103 #define TSMTHREAD_FAIL(msg) errln("%s at file %s, line %d", msg, __FILE__, __LINE__)
104 #define TSMTHREAD_ASSERT(expr) {if (!(expr)) {TSMTHREAD_FAIL("Fail");}}
105
106 MultithreadTest::MultithreadTest()
107 {
108 }
109
110 MultithreadTest::~MultithreadTest()
111 {
112 }
113
114
115
116 #if (ICU_USE_THREADS==0)
117 void MultithreadTest::runIndexedTest( int32_t index, UBool exec,
118 const char* &name, char* /*par*/ ) {
119 if (exec) logln("TestSuite MultithreadTest: ");
120
121 if(index == 0)
122 name = "NO_THREADED_TESTS";
123 else
124 name = "";
125
126 if(exec) { logln("MultithreadTest - test DISABLED. ICU_USE_THREADS set to 0, check your configuration if this is a problem..");
127 }
128 }
129 #else
130
131 #include <stdio.h>
132 #include <string.h>
133 #include <ctype.h> // tolower, toupper
134
135 #include "unicode/putil.h"
136
137 // for mthreadtest
138 #include "unicode/numfmt.h"
139 #include "unicode/choicfmt.h"
140 #include "unicode/msgfmt.h"
141 #include "unicode/locid.h"
142 #include "unicode/coll.h"
143 #include "unicode/calendar.h"
144 #include "ucaconf.h"
145
146 void SimpleThread::errorFunc() {
147 // *(char *)0 = 3; // Force entry into a debugger via a crash;
148 }
149
150 void MultithreadTest::runIndexedTest( int32_t index, UBool exec,
151 const char* &name, char* /*par*/ ) {
152 if (exec)
153 logln("TestSuite MultithreadTest: ");
154 switch (index) {
155 case 0:
156 name = "TestThreads";
157 if (exec)
158 TestThreads();
159 break;
160
161 case 1:
162 name = "TestMutex";
163 if (exec)
164 TestMutex();
165 break;
166
167 case 2:
168 name = "TestThreadedIntl";
169 #if !UCONFIG_NO_FORMATTING
170 if (exec) {
171 TestThreadedIntl();
172 }
173 #endif
174 break;
175
176 case 3:
177 name = "TestCollators";
178 #if !UCONFIG_NO_COLLATION
179 if (exec) {
180 TestCollators();
181 }
182 #endif /* #if !UCONFIG_NO_COLLATION */
183 break;
184
185 case 4:
186 name = "TestString";
187 if (exec) {
188 TestString();
189 }
190 break;
191
192 case 5:
193 name = "TestArabicShapingThreads";
194 if (exec) {
195 TestArabicShapingThreads();
196 }
197 break;
198
199 case 6:
200 name = "TestAnyTranslit";
201 if (exec) {
202 TestAnyTranslit();
203 }
204 break;
205
206 case 7:
207 name = "TestConditionVariables";
208 if (exec) {
209 TestConditionVariables();
210 }
211 break;
212 case 8:
213 name = "TestUnifiedCache";
214 if (exec) {
215 TestUnifiedCache();
216 }
217 break;
218 default:
219 name = "";
220 break; //needed to end loop
221 }
222 }
223
224
225 //-----------------------------------------------------------------------------------
226 //
227 // TestThreads -- see if threads really work at all.
228 //
229 // Set up N threads pointing at N chars. When they are started, they will
230 // each sleep 1 second and then set their chars. At the end we make sure they
231 // are all set.
232 //
233 //-----------------------------------------------------------------------------------
234 #define THREADTEST_NRTHREADS 8
235 #define ARABICSHAPE_THREADTEST 30
236
237 class TestThreadsThread : public SimpleThread
238 {
239 public:
240 TestThreadsThread(char* whatToChange) { fWhatToChange = whatToChange; }
241 virtual void run() { SimpleThread::sleep(1000);
242 Mutex m;
243 *fWhatToChange = '*';
244 }
245 private:
246 char *fWhatToChange;
247 };
248 //-----------------------------------------------------------------------------------
249 //
250 // TestArabicShapeThreads -- see if calls to u_shapeArabic in many threads works successfully
251 //
252 // Set up N threads pointing at N chars. When they are started, they will make calls to doTailTest which tests
253 // u_shapeArabic, if the calls are successful it will the set * chars.
254 // At the end we make sure all threads managed to run u_shapeArabic successfully.
255 // This is a unit test for ticket 9473
256 //
257 //-----------------------------------------------------------------------------------
258 class TestArabicShapeThreads : public SimpleThread
259 {
260 public:
261 TestArabicShapeThreads(char* whatToChange) { fWhatToChange = whatToChange;}
262 virtual void run() {
263 if(doTailTest()==TRUE)
264 *fWhatToChange = '*';
265 }
266 private:
267 char *fWhatToChange;
268
269 UBool doTailTest(void) {
270 static const UChar src[] = { 0x0020, 0x0633, 0 };
271 static const UChar dst_old[] = { 0xFEB1, 0x200B,0 };
272 static const UChar dst_new[] = { 0xFEB1, 0xFE73,0 };
273 UChar dst[3] = { 0x0000, 0x0000,0 };
274 int32_t length;
275 UErrorCode status;
276 IntlTest inteltst = IntlTest();
277
278 status = U_ZERO_ERROR;
279 length = u_shapeArabic(src, -1, dst, UPRV_LENGTHOF(dst),
280 U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR, &status);
281 if(U_FAILURE(status)) {
282 inteltst.errln("Fail: status %s\n", u_errorName(status));
283 return FALSE;
284 } else if(length!=2) {
285 inteltst.errln("Fail: len %d expected 3\n", length);
286 return FALSE;
287 } else if(u_strncmp(dst,dst_old,UPRV_LENGTHOF(dst))) {
288 inteltst.errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
289 dst[0],dst[1],dst_old[0],dst_old[1]);
290 return FALSE;
291 }
292
293
294 //"Trying new tail
295 status = U_ZERO_ERROR;
296 length = u_shapeArabic(src, -1, dst, UPRV_LENGTHOF(dst),
297 U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR|U_SHAPE_TAIL_NEW_UNICODE, &status);
298 if(U_FAILURE(status)) {
299 inteltst.errln("Fail: status %s\n", u_errorName(status));
300 return FALSE;
301 } else if(length!=2) {
302 inteltst.errln("Fail: len %d expected 3\n", length);
303 return FALSE;
304 } else if(u_strncmp(dst,dst_new,UPRV_LENGTHOF(dst))) {
305 inteltst.errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
306 dst[0],dst[1],dst_new[0],dst_new[1]);
307 return FALSE;
308 }
309
310
311 return TRUE;
312
313 }
314
315
316 };
317
318 void MultithreadTest::TestThreads()
319 {
320 char threadTestChars[THREADTEST_NRTHREADS + 1];
321 SimpleThread *threads[THREADTEST_NRTHREADS];
322 int32_t numThreadsStarted = 0;
323
324 int32_t i;
325 for(i=0;i<THREADTEST_NRTHREADS;i++)
326 {
327 threadTestChars[i] = ' ';
328 threads[i] = new TestThreadsThread(&threadTestChars[i]);
329 }
330 threadTestChars[THREADTEST_NRTHREADS] = '\0';
331
332 logln("->" + UnicodeString(threadTestChars) + "<- Firing off threads.. ");
333 for(i=0;i<THREADTEST_NRTHREADS;i++)
334 {
335 if (threads[i]->start() != 0) {
336 errln("Error starting thread %d", i);
337 }
338 else {
339 numThreadsStarted++;
340 }
341 SimpleThread::sleep(100);
342 logln(" Subthread started.");
343 }
344
345 logln("Waiting for threads to be set..");
346 if (numThreadsStarted == 0) {
347 errln("No threads could be started for testing!");
348 return;
349 }
350
351 int32_t patience = 40; // seconds to wait
352
353 while(patience--)
354 {
355 int32_t count = 0;
356 umtx_lock(NULL);
357 for(i=0;i<THREADTEST_NRTHREADS;i++)
358 {
359 if(threadTestChars[i] == '*')
360 {
361 count++;
362 }
363 }
364 umtx_unlock(NULL);
365
366 if(count == THREADTEST_NRTHREADS)
367 {
368 logln("->" + UnicodeString(threadTestChars) + "<- Got all threads! cya");
369 for(i=0;i<THREADTEST_NRTHREADS;i++)
370 {
371 delete threads[i];
372 }
373 return;
374 }
375
376 logln("->" + UnicodeString(threadTestChars) + "<- Waiting..");
377 SimpleThread::sleep(500);
378 }
379
380 errln("->" + UnicodeString(threadTestChars) + "<- PATIENCE EXCEEDED!! Still missing some.");
381 for(i=0;i<THREADTEST_NRTHREADS;i++)
382 {
383 delete threads[i];
384 }
385 }
386
387
388 void MultithreadTest::TestArabicShapingThreads()
389 {
390 char threadTestChars[ARABICSHAPE_THREADTEST + 1];
391 SimpleThread *threads[ARABICSHAPE_THREADTEST];
392 int32_t numThreadsStarted = 0;
393
394 int32_t i;
395
396 for(i=0;i<ARABICSHAPE_THREADTEST;i++)
397 {
398 threadTestChars[i] = ' ';
399 threads[i] = new TestArabicShapeThreads(&threadTestChars[i]);
400 }
401 threadTestChars[ARABICSHAPE_THREADTEST] = '\0';
402
403 logln("-> do TestArabicShapingThreads <- Firing off threads.. ");
404 for(i=0;i<ARABICSHAPE_THREADTEST;i++)
405 {
406 if (threads[i]->start() != 0) {
407 errln("Error starting thread %d", i);
408 }
409 else {
410 numThreadsStarted++;
411 }
412 //SimpleThread::sleep(100);
413 logln(" Subthread started.");
414 }
415
416 logln("Waiting for threads to be set..");
417 if (numThreadsStarted == 0) {
418 errln("No threads could be started for testing!");
419 return;
420 }
421
422 int32_t patience = 100; // seconds to wait
423
424 while(patience--)
425 {
426 int32_t count = 0;
427 umtx_lock(NULL);
428 for(i=0;i<ARABICSHAPE_THREADTEST;i++)
429 {
430 if(threadTestChars[i] == '*')
431 {
432 count++;
433 }
434 }
435 umtx_unlock(NULL);
436
437 if(count == ARABICSHAPE_THREADTEST)
438 {
439 logln("->TestArabicShapingThreads <- Got all threads! cya");
440 for(i=0;i<ARABICSHAPE_THREADTEST;i++)
441 {
442 delete threads[i];
443 }
444 return;
445 }
446
447 logln("-> TestArabicShapingThreads <- Waiting..");
448 SimpleThread::sleep(500);
449 }
450
451 errln("-> TestArabicShapingThreads <- PATIENCE EXCEEDED!! Still missing some.");
452 for(i=0;i<ARABICSHAPE_THREADTEST;i++)
453 {
454 delete threads[i];
455 }
456
457 }
458
459
460 //-----------------------------------------------------------------------
461 //
462 // TestMutex - a simple (non-stress) test to verify that ICU mutexes
463 // are actually mutexing. Does not test the use of
464 // mutexes within ICU services, but rather that the
465 // platform's mutex support is at least superficially there.
466 //
467 //----------------------------------------------------------------------
468 static UMutex gTestMutexA = U_MUTEX_INITIALIZER;
469 static UMutex gTestMutexB = U_MUTEX_INITIALIZER;
470
471 static int gThreadsStarted = 0;
472 static int gThreadsInMiddle = 0;
473 static int gThreadsDone = 0;
474
475 static const int TESTMUTEX_THREAD_COUNT = 4;
476
477 static int safeIncr(int &var, int amt) {
478 // Thread safe (using global mutex) increment of a variable.
479 // Return the updated value.
480 // Can also be used as a safe load of a variable by incrementing it by 0.
481 Mutex m;
482 var += amt;
483 return var;
484 }
485
486 class TestMutexThread : public SimpleThread
487 {
488 public:
489 virtual void run()
490 {
491 // This is the code that each of the spawned threads runs.
492 // All of the spawned threads bunch up together at each of the two mutexes
493 // because the main holds the mutexes until they do.
494 //
495 safeIncr(gThreadsStarted, 1);
496 umtx_lock(&gTestMutexA);
497 umtx_unlock(&gTestMutexA);
498 safeIncr(gThreadsInMiddle, 1);
499 umtx_lock(&gTestMutexB);
500 umtx_unlock(&gTestMutexB);
501 safeIncr(gThreadsDone, 1);
502 }
503 };
504
505 void MultithreadTest::TestMutex()
506 {
507 // Start up the test threads. They should all pile up waiting on
508 // gTestMutexA, which we (the main thread) hold until the test threads
509 // all get there.
510 gThreadsStarted = 0;
511 gThreadsInMiddle = 0;
512 gThreadsDone = 0;
513 umtx_lock(&gTestMutexA);
514 TestMutexThread *threads[TESTMUTEX_THREAD_COUNT];
515 int i;
516 int32_t numThreadsStarted = 0;
517 for (i=0; i<TESTMUTEX_THREAD_COUNT; i++) {
518 threads[i] = new TestMutexThread;
519 if (threads[i]->start() != 0) {
520 errln("Error starting thread %d", i);
521 }
522 else {
523 numThreadsStarted++;
524 }
525 }
526 if (numThreadsStarted == 0) {
527 errln("No threads could be started for testing!");
528 return;
529 }
530
531 int patience = 0;
532 while (safeIncr(gThreadsStarted, 0) != TESTMUTEX_THREAD_COUNT) {
533 if (patience++ > 24) {
534 TSMTHREAD_FAIL("Patience Exceeded");
535 return;
536 }
537 SimpleThread::sleep(500);
538 }
539 // None of the test threads should have advanced past the first mutex.
540 TSMTHREAD_ASSERT(gThreadsInMiddle==0);
541 TSMTHREAD_ASSERT(gThreadsDone==0);
542
543 // All of the test threads have made it to the first mutex.
544 // We (the main thread) now let them advance to the second mutex,
545 // where they should all pile up again.
546 umtx_lock(&gTestMutexB);
547 umtx_unlock(&gTestMutexA);
548
549 patience = 0;
550 while (safeIncr(gThreadsInMiddle, 0) != TESTMUTEX_THREAD_COUNT) {
551 if (patience++ > 24) {
552 TSMTHREAD_FAIL("Patience Exceeded");
553 return;
554 }
555 SimpleThread::sleep(500);
556 }
557 TSMTHREAD_ASSERT(gThreadsDone==0);
558
559 // All test threads made it to the second mutex.
560 // Now let them proceed from there. They will all terminate.
561 umtx_unlock(&gTestMutexB);
562 patience = 0;
563 while (safeIncr(gThreadsDone, 0) != TESTMUTEX_THREAD_COUNT) {
564 if (patience++ > 24) {
565 TSMTHREAD_FAIL("Patience Exceeded");
566 return;
567 }
568 SimpleThread::sleep(500);
569 }
570
571 // All threads made it by both mutexes.
572
573 for (i=0; i<TESTMUTEX_THREAD_COUNT; i++) {
574 delete threads[i];
575 }
576
577 }
578
579
580 //-------------------------------------------------------------------------------------------
581 //
582 // class ThreadWithStatus - a thread that we can check the status and error condition of
583 //
584 //-------------------------------------------------------------------------------------------
585 class ThreadWithStatus : public SimpleThread
586 {
587 public:
588 UBool getError() { return (fErrors > 0); }
589 UBool getError(UnicodeString& fillinError) { fillinError = fErrorString; return (fErrors > 0); }
590 virtual ~ThreadWithStatus(){}
591 protected:
592 ThreadWithStatus() : fErrors(0) {}
593 void error(const UnicodeString &error) {
594 fErrors++; fErrorString = error;
595 SimpleThread::errorFunc();
596 }
597 void error() { error("An error occured."); }
598 private:
599 int32_t fErrors;
600 UnicodeString fErrorString;
601 };
602
603
604
605 //-------------------------------------------------------------------------------------------
606 //
607 // TestMultithreadedIntl. Test ICU Formatting n a multi-threaded environment
608 //
609 //-------------------------------------------------------------------------------------------
610
611
612 // * Show exactly where the string's differences lie.
613 UnicodeString showDifference(const UnicodeString& expected, const UnicodeString& result)
614 {
615 UnicodeString res;
616 res = expected + "<Expected\n";
617 if(expected.length() != result.length())
618 res += " [ Different lengths ] \n";
619 else
620 {
621 for(int32_t i=0;i<expected.length();i++)
622 {
623 if(expected[i] == result[i])
624 {
625 res += " ";
626 }
627 else
628 {
629 res += "|";
630 }
631 }
632 res += "<Differences";
633 res += "\n";
634 }
635 res += result + "<Result\n";
636
637 return res;
638 }
639
640
641
642
643 //-------------------------------------------------------------------------------------------
644 //
645 // FormatThreadTest - a thread that tests performing a number of numberformats.
646 //
647 //-------------------------------------------------------------------------------------------
648
649 const int kFormatThreadIterations = 100; // # of iterations per thread
650 const int kFormatThreadThreads = 10; // # of threads to spawn
651
652 #if !UCONFIG_NO_FORMATTING
653
654
655
656 struct FormatThreadTestData
657 {
658 double number;
659 UnicodeString string;
660 FormatThreadTestData(double a, const UnicodeString& b) : number(a),string(b) {}
661 } ;
662
663
664 // "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3 number,currency}."
665
666 static void formatErrorMessage(UErrorCode &realStatus, const UnicodeString& pattern, const Locale& theLocale,
667 UErrorCode inStatus0, /* statusString 1 */ const Locale &inCountry2, double currency3, // these numbers are the message arguments.
668 UnicodeString &result)
669 {
670 if(U_FAILURE(realStatus))
671 return; // you messed up
672
673 UnicodeString errString1(u_errorName(inStatus0));
674
675 UnicodeString countryName2;
676 inCountry2.getDisplayCountry(theLocale,countryName2);
677
678 Formattable myArgs[] = {
679 Formattable((int32_t)inStatus0), // inStatus0 {0}
680 Formattable(errString1), // statusString1 {1}
681 Formattable(countryName2), // inCountry2 {2}
682 Formattable(currency3)// currency3 {3,number,currency}
683 };
684
685 MessageFormat *fmt = new MessageFormat("MessageFormat's API is broken!!!!!!!!!!!",realStatus);
686 fmt->setLocale(theLocale);
687 fmt->applyPattern(pattern, realStatus);
688
689 if (U_FAILURE(realStatus)) {
690 delete fmt;
691 return;
692 }
693
694 FieldPosition ignore = 0;
695 fmt->format(myArgs,4,result,ignore,realStatus);
696
697 delete fmt;
698 }
699
700 /**
701 * Shared formatters & data used by instances of ThreadSafeFormat.
702 * Exactly one instance of this class is created, and it is then shared concurrently
703 * by the multiple instances of ThreadSafeFormat.
704 */
705 class ThreadSafeFormatSharedData {
706 public:
707 ThreadSafeFormatSharedData(UErrorCode &status);
708 ~ThreadSafeFormatSharedData();
709 LocalPointer<NumberFormat> fFormat;
710 Formattable fYDDThing;
711 Formattable fBBDThing;
712 UnicodeString fYDDStr;
713 UnicodeString fBBDStr;
714 };
715
716 const ThreadSafeFormatSharedData *gSharedData = NULL;
717
718 ThreadSafeFormatSharedData::ThreadSafeFormatSharedData(UErrorCode &status) {
719 fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status));
720 static const UChar kYDD[] = { 0x59, 0x44, 0x44, 0x00 };
721 static const UChar kBBD[] = { 0x42, 0x42, 0x44, 0x00 };
722 fYDDThing.adoptObject(new CurrencyAmount(123.456, kYDD, status));
723 fBBDThing.adoptObject(new CurrencyAmount(987.654, kBBD, status));
724 if (U_FAILURE(status)) {
725 return;
726 }
727 fFormat->format(fYDDThing, fYDDStr, NULL, status);
728 fFormat->format(fBBDThing, fBBDStr, NULL, status);
729 gSharedData = this;
730 }
731
732 ThreadSafeFormatSharedData::~ThreadSafeFormatSharedData() {
733 gSharedData = NULL;
734 }
735
736 /**
737 * Class for thread-safe testing of format.
738 * Instances of this class appear as members of class FormatThreadTest.
739 * Multiple instances of FormatThreadTest coexist.
740 * ThreadSafeFormat::doStuff() is called concurrently to test the thread safety of
741 * various shared format operations.
742 */
743 class ThreadSafeFormat {
744 public:
745 /* give a unique offset to each thread */
746 ThreadSafeFormat(UErrorCode &status);
747 UBool doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const;
748 private:
749 LocalPointer<NumberFormat> fFormat; // formatter - en_US constructed currency
750 };
751
752
753 ThreadSafeFormat::ThreadSafeFormat(UErrorCode &status) {
754 fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status));
755 }
756
757 static const UChar kUSD[] = { 0x55, 0x53, 0x44, 0x00 };
758
759 UBool ThreadSafeFormat::doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const {
760 UBool okay = TRUE;
761
762 if(u_strcmp(fFormat->getCurrency(), kUSD)) {
763 appendErr.append("fFormat currency != ")
764 .append(kUSD)
765 .append(", =")
766 .append(fFormat->getCurrency())
767 .append("! ");
768 okay = FALSE;
769 }
770
771 if(u_strcmp(gSharedData->fFormat->getCurrency(), kUSD)) {
772 appendErr.append("gFormat currency != ")
773 .append(kUSD)
774 .append(", =")
775 .append(gSharedData->fFormat->getCurrency())
776 .append("! ");
777 okay = FALSE;
778 }
779 UnicodeString str;
780 const UnicodeString *o=NULL;
781 Formattable f;
782 const NumberFormat *nf = NULL; // only operate on it as const.
783 switch(offset%4) {
784 case 0: f = gSharedData->fYDDThing; o = &gSharedData->fYDDStr; nf = gSharedData->fFormat.getAlias(); break;
785 case 1: f = gSharedData->fBBDThing; o = &gSharedData->fBBDStr; nf = gSharedData->fFormat.getAlias(); break;
786 case 2: f = gSharedData->fYDDThing; o = &gSharedData->fYDDStr; nf = fFormat.getAlias(); break;
787 case 3: f = gSharedData->fBBDThing; o = &gSharedData->fBBDStr; nf = fFormat.getAlias(); break;
788 }
789 nf->format(f, str, NULL, status);
790
791 if(*o != str) {
792 appendErr.append(showDifference(*o, str));
793 okay = FALSE;
794 }
795 return okay;
796 }
797
798 UBool U_CALLCONV isAcceptable(void *, const char *, const char *, const UDataInfo *) {
799 return TRUE;
800 }
801
802 //static UMTX debugMutex = NULL;
803 //static UMTX gDebugMutex;
804
805
806 class FormatThreadTest : public ThreadWithStatus
807 {
808 public:
809 int fNum;
810 int fTraceInfo;
811
812 LocalPointer<ThreadSafeFormat> fTSF;
813
814 FormatThreadTest() // constructor is NOT multithread safe.
815 : ThreadWithStatus(),
816 fNum(0),
817 fTraceInfo(0),
818 fTSF(NULL),
819 fOffset(0)
820 // the locale to use
821 {
822 UErrorCode status = U_ZERO_ERROR; // TODO: rearrange code to allow checking of status.
823 fTSF.adoptInstead(new ThreadSafeFormat(status));
824 static int32_t fgOffset = 0;
825 fgOffset += 3;
826 fOffset = fgOffset;
827 }
828
829
830 virtual void run()
831 {
832 fTraceInfo = 1;
833 LocalPointer<NumberFormat> percentFormatter;
834 UErrorCode status = U_ZERO_ERROR;
835
836 #if 0
837 // debugging code,
838 for (int i=0; i<4000; i++) {
839 status = U_ZERO_ERROR;
840 UDataMemory *data1 = udata_openChoice(0, "res", "en_US", isAcceptable, 0, &status);
841 UDataMemory *data2 = udata_openChoice(0, "res", "fr", isAcceptable, 0, &status);
842 udata_close(data1);
843 udata_close(data2);
844 if (U_FAILURE(status)) {
845 error("udata_openChoice failed.\n");
846 break;
847 }
848 }
849 return;
850 #endif
851
852 #if 0
853 // debugging code,
854 int m;
855 for (m=0; m<4000; m++) {
856 status = U_ZERO_ERROR;
857 UResourceBundle *res = NULL;
858 const char *localeName = NULL;
859
860 Locale loc = Locale::getEnglish();
861
862 localeName = loc.getName();
863 // localeName = "en";
864
865 // ResourceBundle bund = ResourceBundle(0, loc, status);
866 //umtx_lock(&gDebugMutex);
867 res = ures_open(NULL, localeName, &status);
868 //umtx_unlock(&gDebugMutex);
869
870 //umtx_lock(&gDebugMutex);
871 ures_close(res);
872 //umtx_unlock(&gDebugMutex);
873
874 if (U_FAILURE(status)) {
875 error("Resource bundle construction failed.\n");
876 break;
877 }
878 }
879 return;
880 #endif
881
882 // Keep this data here to avoid static initialization.
883 FormatThreadTestData kNumberFormatTestData[] =
884 {
885 FormatThreadTestData((double)5.0, UnicodeString("5", "")),
886 FormatThreadTestData( 6.0, UnicodeString("6", "")),
887 FormatThreadTestData( 20.0, UnicodeString("20", "")),
888 FormatThreadTestData( 8.0, UnicodeString("8", "")),
889 FormatThreadTestData( 8.3, UnicodeString("8.3", "")),
890 FormatThreadTestData( 12345, UnicodeString("12,345", "")),
891 FormatThreadTestData( 81890.23, UnicodeString("81,890.23", "")),
892 };
893 int32_t kNumberFormatTestDataLength = UPRV_LENGTHOF(kNumberFormatTestData);
894
895 // Keep this data here to avoid static initialization.
896 FormatThreadTestData kPercentFormatTestData[] =
897 {
898 FormatThreadTestData((double)5.0, CharsToUnicodeString("500\\u00a0%")),
899 FormatThreadTestData( 1.0, CharsToUnicodeString("100\\u00a0%")),
900 FormatThreadTestData( 0.26, CharsToUnicodeString("26\\u00a0%")),
901 FormatThreadTestData(
902 16384.99, CharsToUnicodeString("1\\u00a0638\\u00a0499\\u00a0%")), // U+00a0 = NBSP
903 FormatThreadTestData(
904 81890.23, CharsToUnicodeString("8\\u00a0189\\u00a0023\\u00a0%")),
905 };
906 int32_t kPercentFormatTestDataLength = UPRV_LENGTHOF(kPercentFormatTestData);
907 int32_t iteration;
908
909 status = U_ZERO_ERROR;
910 LocalPointer<NumberFormat> formatter(NumberFormat::createInstance(Locale::getEnglish(),status));
911 if(U_FAILURE(status)) {
912 error("Error on NumberFormat::createInstance().");
913 goto cleanupAndReturn;
914 }
915
916 percentFormatter.adoptInstead(NumberFormat::createPercentInstance(Locale::getFrench(),status));
917 if(U_FAILURE(status)) {
918 error("Error on NumberFormat::createPercentInstance().");
919 goto cleanupAndReturn;
920 }
921
922 for(iteration = 0;!getError() && iteration<kFormatThreadIterations;iteration++)
923 {
924
925 int32_t whichLine = (iteration + fOffset)%kNumberFormatTestDataLength;
926
927 UnicodeString output;
928
929 formatter->format(kNumberFormatTestData[whichLine].number, output);
930
931 if(0 != output.compare(kNumberFormatTestData[whichLine].string)) {
932 error("format().. expected " + kNumberFormatTestData[whichLine].string
933 + " got " + output);
934 goto cleanupAndReturn;
935 }
936
937 // Now check percent.
938 output.remove();
939 whichLine = (iteration + fOffset)%kPercentFormatTestDataLength;
940
941 percentFormatter->format(kPercentFormatTestData[whichLine].number, output);
942 if(0 != output.compare(kPercentFormatTestData[whichLine].string))
943 {
944 error("percent format().. \n" +
945 showDifference(kPercentFormatTestData[whichLine].string,output));
946 goto cleanupAndReturn;
947 }
948
949 // Test message error
950 const int kNumberOfMessageTests = 3;
951 UErrorCode statusToCheck;
952 UnicodeString patternToCheck;
953 Locale messageLocale;
954 Locale countryToCheck;
955 double currencyToCheck;
956
957 UnicodeString expected;
958
959 // load the cases.
960 switch((iteration+fOffset) % kNumberOfMessageTests)
961 {
962 default:
963 case 0:
964 statusToCheck= U_FILE_ACCESS_ERROR;
965 patternToCheck= "0:Someone from {2} is receiving a #{0}"
966 " error - {1}. Their telephone call is costing "
967 "{3,number,currency}."; // number,currency
968 messageLocale= Locale("en","US");
969 countryToCheck= Locale("","HR");
970 currencyToCheck= 8192.77;
971 expected= "0:Someone from Croatia is receiving a #4 error - "
972 "U_FILE_ACCESS_ERROR. Their telephone call is costing $8,192.77.";
973 break;
974 case 1:
975 statusToCheck= U_INDEX_OUTOFBOUNDS_ERROR;
976 patternToCheck= "1:A customer in {2} is receiving a #{0} error - {1}. "
977 "Their telephone call is costing {3,number,currency}."; // number,currency
978 messageLocale= Locale("de","DE@currency=DEM");
979 countryToCheck= Locale("","BF");
980 currencyToCheck= 2.32;
981 expected= CharsToUnicodeString(
982 "1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. "
983 "Their telephone call is costing 2,32\\u00A0DM.");
984 break;
985 case 2:
986 statusToCheck= U_MEMORY_ALLOCATION_ERROR;
987 patternToCheck= "2:user in {2} is receiving a #{0} error - {1}. "
988 "They insist they just spent {3,number,currency} "
989 "on memory."; // number,currency
990 messageLocale= Locale("de","AT@currency=ATS"); // Austrian German
991 countryToCheck= Locale("","US"); // hmm
992 currencyToCheck= 40193.12;
993 expected= CharsToUnicodeString(
994 "2:user in Vereinigte Staaten is receiving a #7 error"
995 " - U_MEMORY_ALLOCATION_ERROR. They insist they just spent"
996 " \\u00f6S\\u00A040.193,12 on memory.");
997 break;
998 }
999
1000 UnicodeString result;
1001 UErrorCode status = U_ZERO_ERROR;
1002 formatErrorMessage(status,patternToCheck,messageLocale,statusToCheck,
1003 countryToCheck,currencyToCheck,result);
1004 if(U_FAILURE(status))
1005 {
1006 UnicodeString tmp(u_errorName(status));
1007 error("Failure on message format, pattern=" + patternToCheck +
1008 ", error = " + tmp);
1009 goto cleanupAndReturn;
1010 }
1011
1012 if(result != expected)
1013 {
1014 error("PatternFormat: \n" + showDifference(expected,result));
1015 goto cleanupAndReturn;
1016 }
1017 // test the Thread Safe Format
1018 UnicodeString appendErr;
1019 if(!fTSF->doStuff(fNum, appendErr, status)) {
1020 error(appendErr);
1021 goto cleanupAndReturn;
1022 }
1023 } /* end of for loop */
1024
1025
1026
1027 cleanupAndReturn:
1028 // while (fNum == 4) {SimpleThread::sleep(10000);} // Force a failure by preventing thread from finishing
1029 fTraceInfo = 2;
1030 }
1031
1032 private:
1033 int32_t fOffset; // where we are testing from.
1034 };
1035
1036 // ** The actual test function.
1037
1038 void MultithreadTest::TestThreadedIntl()
1039 {
1040 int i;
1041 UnicodeString theErr;
1042 UBool haveDisplayedInfo[kFormatThreadThreads];
1043 static const int32_t PATIENCE_SECONDS = 45;
1044
1045 UErrorCode threadSafeErr = U_ZERO_ERROR;
1046
1047 ThreadSafeFormatSharedData sharedData(threadSafeErr);
1048 assertSuccess("initializing ThreadSafeFormat", threadSafeErr, TRUE);
1049
1050 //
1051 // Create and start the test threads
1052 //
1053 logln("Spawning: %d threads * %d iterations each.",
1054 kFormatThreadThreads, kFormatThreadIterations);
1055 LocalArray<FormatThreadTest> tests(new FormatThreadTest[kFormatThreadThreads]);
1056 for(int32_t j = 0; j < kFormatThreadThreads; j++) {
1057 tests[j].fNum = j;
1058 int32_t threadStatus = tests[j].start();
1059 if (threadStatus != 0) {
1060 errln("System Error %d starting thread number %d.", threadStatus, j);
1061 SimpleThread::errorFunc();
1062 return;
1063 }
1064 haveDisplayedInfo[j] = FALSE;
1065 }
1066
1067
1068 // Spin, waiting for the test threads to finish.
1069 UBool stillRunning;
1070 UDate startTime, endTime;
1071 startTime = Calendar::getNow();
1072 double lastComplaint = 0;
1073 do {
1074 /* Spin until the test threads complete. */
1075 stillRunning = FALSE;
1076 endTime = Calendar::getNow();
1077 double elapsedSeconds = ((int32_t)(endTime - startTime)/U_MILLIS_PER_SECOND);
1078 if (elapsedSeconds > PATIENCE_SECONDS) {
1079 errln("Patience exceeded. Test is taking too long.");
1080 return;
1081 } else if((elapsedSeconds-lastComplaint) > 2.0) {
1082 infoln("%.1f seconds elapsed (still waiting..)", elapsedSeconds);
1083 lastComplaint = elapsedSeconds;
1084 }
1085 /*
1086 The following sleep must be here because the *BSD operating systems
1087 have a brain dead thread scheduler. They starve the child threads from
1088 CPU time.
1089 */
1090 SimpleThread::sleep(1); // yield
1091 for(i=0;i<kFormatThreadThreads;i++) {
1092 if (tests[i].isRunning()) {
1093 stillRunning = TRUE;
1094 } else if (haveDisplayedInfo[i] == FALSE) {
1095 logln("Thread # %d is complete..", i);
1096 if(tests[i].getError(theErr)) {
1097 dataerrln(UnicodeString("#") + i + ": " + theErr);
1098 SimpleThread::errorFunc();
1099 }
1100 haveDisplayedInfo[i] = TRUE;
1101 }
1102 }
1103 } while (stillRunning);
1104
1105 //
1106 // All threads have finished.
1107 //
1108 assertSuccess("finalizing ThreadSafeFormat", threadSafeErr, TRUE);
1109 }
1110 #endif /* #if !UCONFIG_NO_FORMATTING */
1111
1112
1113
1114
1115
1116 //-------------------------------------------------------------------------------------------
1117 //
1118 // Collation threading test
1119 //
1120 //-------------------------------------------------------------------------------------------
1121 #if !UCONFIG_NO_COLLATION
1122
1123 #define kCollatorThreadThreads 10 // # of threads to spawn
1124 #define kCollatorThreadPatience kCollatorThreadThreads*30
1125
1126 struct Line {
1127 UChar buff[25];
1128 int32_t buflen;
1129 } ;
1130
1131 static UBool
1132 skipLineBecauseOfBug(const UChar *s, int32_t length) {
1133 // TODO: Fix ICU ticket #8052
1134 if(length >= 3 &&
1135 (s[0] == 0xfb2 || s[0] == 0xfb3) &&
1136 s[1] == 0x334 &&
1137 (s[2] == 0xf73 || s[2] == 0xf75 || s[2] == 0xf81)) {
1138 return TRUE;
1139 }
1140 return FALSE;
1141 }
1142
1143 static UCollationResult
1144 normalizeResult(int32_t result) {
1145 return result<0 ? UCOL_LESS : result==0 ? UCOL_EQUAL : UCOL_GREATER;
1146 }
1147
1148 class CollatorThreadTest : public ThreadWithStatus
1149 {
1150 private:
1151 const Collator *coll;
1152 const Line *lines;
1153 int32_t noLines;
1154 UBool isAtLeastUCA62;
1155 public:
1156 CollatorThreadTest() : ThreadWithStatus(),
1157 coll(NULL),
1158 lines(NULL),
1159 noLines(0),
1160 isAtLeastUCA62(TRUE)
1161 {
1162 };
1163 void setCollator(Collator *c, Line *l, int32_t nl, UBool atLeastUCA62)
1164 {
1165 coll = c;
1166 lines = l;
1167 noLines = nl;
1168 isAtLeastUCA62 = atLeastUCA62;
1169 }
1170 virtual void run() {
1171 uint8_t sk1[1024], sk2[1024];
1172 uint8_t *oldSk = NULL, *newSk = sk1;
1173 int32_t oldLen = 0;
1174 int32_t prev = 0;
1175 int32_t i = 0;
1176
1177 for(i = 0; i < noLines; i++) {
1178 if(lines[i].buflen == 0) { continue; }
1179
1180 if(skipLineBecauseOfBug(lines[i].buff, lines[i].buflen)) { continue; }
1181
1182 int32_t resLen = coll->getSortKey(lines[i].buff, lines[i].buflen, newSk, 1024);
1183
1184 if(oldSk != NULL) {
1185 int32_t skres = strcmp((char *)oldSk, (char *)newSk);
1186 int32_t cmpres = coll->compare(lines[prev].buff, lines[prev].buflen, lines[i].buff, lines[i].buflen);
1187 int32_t cmpres2 = coll->compare(lines[i].buff, lines[i].buflen, lines[prev].buff, lines[prev].buflen);
1188
1189 if(cmpres != -cmpres2) {
1190 error(UnicodeString("Compare result not symmetrical on line ") + (i + 1));
1191 break;
1192 }
1193
1194 if(cmpres != normalizeResult(skres)) {
1195 error(UnicodeString("Difference between coll->compare and sortkey compare on line ") + (i + 1));
1196 break;
1197 }
1198
1199 int32_t res = cmpres;
1200 if(res == 0 && !isAtLeastUCA62) {
1201 // Up to UCA 6.1, the collation test files use a custom tie-breaker,
1202 // comparing the raw input strings.
1203 res = u_strcmpCodePointOrder(lines[prev].buff, lines[i].buff);
1204 // Starting with UCA 6.2, the collation test files use the standard UCA tie-breaker,
1205 // comparing the NFD versions of the input strings,
1206 // which we do via setting strength=identical.
1207 }
1208 if(res > 0) {
1209 error(UnicodeString("Line is not greater or equal than previous line, for line ") + (i + 1));
1210 break;
1211 }
1212 }
1213
1214 oldSk = newSk;
1215 oldLen = resLen;
1216 (void)oldLen; // Suppress set but not used warning.
1217 prev = i;
1218
1219 newSk = (newSk == sk1)?sk2:sk1;
1220 }
1221 }
1222 };
1223
1224 void MultithreadTest::TestCollators()
1225 {
1226
1227 UErrorCode status = U_ZERO_ERROR;
1228 FILE *testFile = NULL;
1229 char testDataPath[1024];
1230 strcpy(testDataPath, IntlTest::getSourceTestData(status));
1231 if (U_FAILURE(status)) {
1232 errln("ERROR: could not open test data %s", u_errorName(status));
1233 return;
1234 }
1235 strcat(testDataPath, "CollationTest_");
1236
1237 const char* type = "NON_IGNORABLE";
1238
1239 const char *ext = ".txt";
1240 if(testFile) {
1241 fclose(testFile);
1242 }
1243 char buffer[1024];
1244 strcpy(buffer, testDataPath);
1245 strcat(buffer, type);
1246 size_t bufLen = strlen(buffer);
1247
1248 // we try to open 3 files:
1249 // path/CollationTest_type.txt
1250 // path/CollationTest_type_SHORT.txt
1251 // path/CollationTest_type_STUB.txt
1252 // we are going to test with the first one that we manage to open.
1253
1254 strcpy(buffer+bufLen, ext);
1255
1256 testFile = fopen(buffer, "rb");
1257
1258 if(testFile == 0) {
1259 strcpy(buffer+bufLen, "_SHORT");
1260 strcat(buffer, ext);
1261 testFile = fopen(buffer, "rb");
1262
1263 if(testFile == 0) {
1264 strcpy(buffer+bufLen, "_STUB");
1265 strcat(buffer, ext);
1266 testFile = fopen(buffer, "rb");
1267
1268 if (testFile == 0) {
1269 *(buffer+bufLen) = 0;
1270 dataerrln("could not open any of the conformance test files, tried opening base %s", buffer);
1271 return;
1272 } else {
1273 infoln(
1274 "INFO: Working with the stub file.\n"
1275 "If you need the full conformance test, please\n"
1276 "download the appropriate data files from:\n"
1277 "http://source.icu-project.org/repos/icu/tools/trunk/unicodetools/com/ibm/text/data/");
1278 }
1279 }
1280 }
1281
1282 LocalArray<Line> lines(new Line[200000]);
1283 memset(lines.getAlias(), 0, sizeof(Line)*200000);
1284 int32_t lineNum = 0;
1285
1286 UChar bufferU[1024];
1287 uint32_t first = 0;
1288
1289 while (fgets(buffer, 1024, testFile) != NULL) {
1290 if(*buffer == 0 || buffer[0] == '#') {
1291 // Store empty and comment lines so that errors are reported
1292 // for the real test file lines.
1293 lines[lineNum].buflen = 0;
1294 lines[lineNum].buff[0] = 0;
1295 } else {
1296 int32_t buflen = u_parseString(buffer, bufferU, 1024, &first, &status);
1297 lines[lineNum].buflen = buflen;
1298 u_memcpy(lines[lineNum].buff, bufferU, buflen);
1299 lines[lineNum].buff[buflen] = 0;
1300 }
1301 lineNum++;
1302 }
1303 fclose(testFile);
1304 if(U_FAILURE(status)) {
1305 dataerrln("Couldn't read the test file!");
1306 return;
1307 }
1308
1309 UVersionInfo uniVersion;
1310 static const UVersionInfo v62 = { 6, 2, 0, 0 };
1311 u_getUnicodeVersion(uniVersion);
1312 UBool isAtLeastUCA62 = uprv_memcmp(uniVersion, v62, 4) >= 0;
1313
1314 LocalPointer<Collator> coll(Collator::createInstance(Locale::getRoot(), status));
1315 if(U_FAILURE(status)) {
1316 errcheckln(status, "Couldn't open UCA collator");
1317 return;
1318 }
1319 coll->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
1320 coll->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status);
1321 coll->setAttribute(UCOL_CASE_LEVEL, UCOL_OFF, status);
1322 coll->setAttribute(UCOL_STRENGTH, isAtLeastUCA62 ? UCOL_IDENTICAL : UCOL_TERTIARY, status);
1323 coll->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE, status);
1324
1325 int32_t noSpawned = 0;
1326 int32_t spawnResult = 0;
1327 LocalArray<CollatorThreadTest> tests(new CollatorThreadTest[kCollatorThreadThreads]);
1328
1329 logln(UnicodeString("Spawning: ") + kCollatorThreadThreads + " threads * " + kFormatThreadIterations + " iterations each.");
1330 int32_t j = 0;
1331 for(j = 0; j < kCollatorThreadThreads; j++) {
1332 //logln("Setting collator %i", j);
1333 tests[j].setCollator(coll.getAlias(), lines.getAlias(), lineNum, isAtLeastUCA62);
1334 }
1335 for(j = 0; j < kCollatorThreadThreads; j++) {
1336 log("%i ", j);
1337 spawnResult = tests[j].start();
1338 if(spawnResult != 0) {
1339 infoln("THREAD INFO: Couldn't spawn more than %i threads", noSpawned);
1340 break;
1341 }
1342 noSpawned++;
1343 }
1344 logln("Spawned all");
1345 if (noSpawned == 0) {
1346 errln("No threads could be spawned.");
1347 return;
1348 }
1349
1350 for(int32_t patience = kCollatorThreadPatience;patience > 0; patience --)
1351 {
1352 logln("Waiting...");
1353
1354 int32_t i;
1355 int32_t terrs = 0;
1356 int32_t completed =0;
1357
1358 for(i=0;i<kCollatorThreadThreads;i++)
1359 {
1360 if (tests[i].isRunning() == FALSE)
1361 {
1362 completed++;
1363
1364 //logln(UnicodeString("Test #") + i + " is complete.. ");
1365
1366 UnicodeString theErr;
1367 if(tests[i].getError(theErr))
1368 {
1369 terrs++;
1370 errln(UnicodeString("#") + i + ": " + theErr);
1371 }
1372 // print out the error, too, if any.
1373 }
1374 }
1375 logln("Completed %i tests", completed);
1376
1377 if(completed == noSpawned)
1378 {
1379 logln("Done! All %i tests are finished", noSpawned);
1380
1381 if(terrs)
1382 {
1383 errln("There were errors.");
1384 SimpleThread::errorFunc();
1385 }
1386 return;
1387 }
1388
1389 SimpleThread::sleep(900);
1390 }
1391 errln("patience exceeded. ");
1392 SimpleThread::errorFunc();
1393 }
1394
1395 #endif /* #if !UCONFIG_NO_COLLATION */
1396
1397
1398
1399
1400 //-------------------------------------------------------------------------------------------
1401 //
1402 // StringThreadTest2
1403 //
1404 //-------------------------------------------------------------------------------------------
1405
1406 const int kStringThreadIterations = 2500;// # of iterations per thread
1407 const int kStringThreadThreads = 10; // # of threads to spawn
1408 const int kStringThreadPatience = 120; // time in seconds to wait for all threads
1409
1410
1411 class StringThreadTest2 : public ThreadWithStatus
1412 {
1413 public:
1414 int fNum;
1415 int fTraceInfo;
1416 const UnicodeString *fSharedString;
1417
1418 StringThreadTest2(const UnicodeString *sharedString, int num) // constructor is NOT multithread safe.
1419 : ThreadWithStatus(),
1420 fNum(num),
1421 fTraceInfo(0),
1422 fSharedString(sharedString)
1423 {
1424 };
1425
1426
1427 virtual void run()
1428 {
1429 fTraceInfo = 1;
1430 int loopCount = 0;
1431
1432 for (loopCount = 0; loopCount < kStringThreadIterations; loopCount++) {
1433 if (*fSharedString != "This is the original test string.") {
1434 error("Original string is corrupt.");
1435 break;
1436 }
1437 UnicodeString s1 = *fSharedString;
1438 s1 += "cat this";
1439 UnicodeString s2(s1);
1440 UnicodeString s3 = *fSharedString;
1441 s2 = s3;
1442 s3.truncate(12);
1443 s2.truncate(0);
1444 }
1445
1446 // while (fNum == 4) {SimpleThread::sleep(10000);} // Force a failure by preventing thread from finishing
1447 fTraceInfo = 2;
1448 }
1449
1450 };
1451
1452 // ** The actual test function.
1453
1454 void MultithreadTest::TestString()
1455 {
1456 int patience;
1457 int terrs = 0;
1458 int j;
1459
1460 UnicodeString *testString = new UnicodeString("This is the original test string.");
1461
1462 // Not using LocalArray<StringThreadTest2> tests[kStringThreadThreads];
1463 // because we don't always want to delete them.
1464 // See the comments below the cleanupAndReturn label.
1465 StringThreadTest2 *tests[kStringThreadThreads];
1466 for(j = 0; j < kStringThreadThreads; j++) {
1467 tests[j] = new StringThreadTest2(testString, j);
1468 }
1469
1470 logln(UnicodeString("Spawning: ") + kStringThreadThreads + " threads * " + kStringThreadIterations + " iterations each.");
1471 for(j = 0; j < kStringThreadThreads; j++) {
1472 int32_t threadStatus = tests[j]->start();
1473 if (threadStatus != 0) {
1474 errln("System Error %d starting thread number %d.", threadStatus, j);
1475 SimpleThread::errorFunc();
1476 goto cleanupAndReturn;
1477 }
1478 }
1479
1480 for(patience = kStringThreadPatience;patience > 0; patience --)
1481 {
1482 logln("Waiting...");
1483
1484 int32_t i;
1485 terrs = 0;
1486 int32_t completed =0;
1487
1488 for(i=0;i<kStringThreadThreads;i++) {
1489 if (tests[i]->isRunning() == FALSE)
1490 {
1491 completed++;
1492
1493 logln(UnicodeString("Test #") + i + " is complete.. ");
1494
1495 UnicodeString theErr;
1496 if(tests[i]->getError(theErr))
1497 {
1498 terrs++;
1499 errln(UnicodeString("#") + i + ": " + theErr);
1500 }
1501 // print out the error, too, if any.
1502 }
1503 }
1504
1505 if(completed == kStringThreadThreads)
1506 {
1507 logln("Done!");
1508 if(terrs) {
1509 errln("There were errors.");
1510 }
1511 break;
1512 }
1513
1514 SimpleThread::sleep(900);
1515 }
1516
1517 if (patience <= 0) {
1518 errln("patience exceeded. ");
1519 // while (TRUE) {SimpleThread::sleep(10000);} // TODO: for debugging. Sleep forever on failure.
1520 terrs++;
1521 }
1522
1523 if (terrs > 0) {
1524 SimpleThread::errorFunc();
1525 }
1526
1527 cleanupAndReturn:
1528 if (terrs == 0) {
1529 /*
1530 Don't clean up if there are errors. This prevents crashes if the
1531 threads are still running and using this data. This will only happen
1532 if there is an error with the test, ICU, or the machine is too slow.
1533 It's better to leak than crash.
1534 */
1535 for(j = 0; j < kStringThreadThreads; j++) {
1536 delete tests[j];
1537 }
1538 delete testString;
1539 }
1540 }
1541
1542
1543 // Test for ticket #10673, race in cache code in AnyTransliterator.
1544 // It's difficult to make the original unsafe code actually fail, but
1545 // this test will fairly reliably take the code path for races in
1546 // populating the cache.
1547
1548 #if !UCONFIG_NO_TRANSLITERATION
1549 class TxThread: public SimpleThread {
1550 private:
1551 Transliterator *fSharedTranslit;
1552 public:
1553 UBool fSuccess;
1554 TxThread(Transliterator *tx) : fSharedTranslit(tx), fSuccess(FALSE) {};
1555 ~TxThread();
1556 void run();
1557 };
1558
1559 TxThread::~TxThread() {}
1560 void TxThread::run() {
1561 UnicodeString greekString("\\u03B4\\u03B9\\u03B1\\u03C6\\u03BF\\u03C1\\u03B5\\u03C4\\u03B9\\u03BA\\u03BF\\u03CD\\u03C2");
1562 greekString = greekString.unescape();
1563 fSharedTranslit->transliterate(greekString);
1564 fSuccess = greekString[0] == 0x64; // 'd'. The whole transliterated string is "diaphoretikous" (accented u).
1565 }
1566 #endif
1567
1568
1569 void MultithreadTest::TestAnyTranslit() {
1570 #if !UCONFIG_NO_TRANSLITERATION
1571 UErrorCode status = U_ZERO_ERROR;
1572 LocalPointer<Transliterator> tx(Transliterator::createInstance("Any-Latin", UTRANS_FORWARD, status));
1573 if (U_FAILURE(status)) {
1574 dataerrln("File %s, Line %d: Error, status = %s", __FILE__, __LINE__, u_errorName(status));
1575 return;
1576 }
1577 TxThread * threads[4];
1578 int32_t i;
1579 for (i=0; i<4; i++) {
1580 threads[i] = new TxThread(tx.getAlias());
1581 }
1582 for (i=0; i<4; i++) {
1583 threads[i]->start();
1584 }
1585 int32_t patience = 100;
1586 UBool success;
1587 UBool someThreadRunning;
1588 do {
1589 someThreadRunning = FALSE;
1590 success = TRUE;
1591 for (i=0; i<4; i++) {
1592 if (threads[i]->isRunning()) {
1593 someThreadRunning = TRUE;
1594 SimpleThread::sleep(10);
1595 break;
1596 } else {
1597 if (threads[i]->fSuccess == FALSE) {
1598 success = FALSE;
1599 }
1600 }
1601 }
1602 } while (someThreadRunning && --patience > 0);
1603
1604 if (patience <= 0) {
1605 errln("File %s, Line %d: Error, one or more threads did not complete.", __FILE__, __LINE__);
1606 }
1607 if (success == FALSE) {
1608 errln("File %s, Line %d: Error, transliteration result incorrect.", __FILE__, __LINE__);
1609 }
1610
1611 for (i=0; i<4; i++) {
1612 delete threads[i];
1613 }
1614 #endif // !UCONFIG_NO_TRANSLITERATION
1615 }
1616
1617
1618 // Condition Variables Test
1619 // Create a swarm of threads.
1620 // Using a mutex and a condition variables each thread
1621 // Increments a global count of started threads.
1622 // Broadcasts that it has started.
1623 // Waits on the condition that all threads have started.
1624 // Increments a global count of finished threads.
1625 // Waits on the condition that all threads have finished.
1626 // Exits.
1627
1628 class CondThread: public SimpleThread {
1629 public:
1630 CondThread() :fFinished(false) {};
1631 ~CondThread() {};
1632 void run();
1633 bool fFinished;
1634 };
1635
1636 static UMutex gCTMutex = U_MUTEX_INITIALIZER;
1637 static UConditionVar gCTConditionVar = U_CONDITION_INITIALIZER;
1638 int gConditionTestOne = 1; // Value one. Non-const, extern linkage to inhibit
1639 // compiler assuming a known value.
1640 int gStartedThreads;
1641 int gFinishedThreads;
1642 static const int NUMTHREADS = 10;
1643
1644 static MultithreadTest *gThisTest = NULL; // Make test frame work functions available to
1645 // non-member functions.
1646
1647 // Worker thread function.
1648 void CondThread::run() {
1649 umtx_lock(&gCTMutex);
1650 gStartedThreads += gConditionTestOne;
1651 umtx_condBroadcast(&gCTConditionVar);
1652
1653 while (gStartedThreads < NUMTHREADS) {
1654 if (gFinishedThreads != 0) {
1655 gThisTest->errln("File %s, Line %d: Error, gStartedThreads = %d, gFinishedThreads = %d",
1656 __FILE__, __LINE__, gStartedThreads, gFinishedThreads);
1657 }
1658 umtx_condWait(&gCTConditionVar, &gCTMutex);
1659 }
1660
1661 gFinishedThreads += gConditionTestOne;
1662 fFinished = true;
1663 umtx_condBroadcast(&gCTConditionVar);
1664
1665 while (gFinishedThreads < NUMTHREADS) {
1666 umtx_condWait(&gCTConditionVar, &gCTMutex);
1667 }
1668 umtx_unlock(&gCTMutex);
1669 }
1670
1671 void MultithreadTest::TestConditionVariables() {
1672 gThisTest = this;
1673 gStartedThreads = 0;
1674 gFinishedThreads = 0;
1675 int i;
1676
1677 umtx_lock(&gCTMutex);
1678 CondThread *threads[NUMTHREADS];
1679 for (i=0; i<NUMTHREADS; ++i) {
1680 threads[i] = new CondThread;
1681 threads[i]->start();
1682 }
1683
1684 while (gStartedThreads < NUMTHREADS) {
1685 umtx_condWait(&gCTConditionVar, &gCTMutex);
1686 }
1687
1688 while (gFinishedThreads < NUMTHREADS) {
1689 umtx_condWait(&gCTConditionVar, &gCTMutex);
1690 }
1691
1692 umtx_unlock(&gCTMutex);
1693
1694 for (i=0; i<NUMTHREADS; ++i) {
1695 if (!threads[i]->fFinished) {
1696 errln("File %s, Line %d: Error, threads[%d]->fFinished == false", __FILE__, __LINE__, i);
1697 }
1698 delete threads[i];
1699 }
1700 }
1701
1702 static const char *gCacheLocales[] = {"en_US", "en_GB", "fr_FR", "fr"};
1703 static int32_t gObjectsCreated = 0;
1704 static const int32_t CACHE_LOAD = 3;
1705
1706 class UCTMultiThreadItem : public SharedObject {
1707 public:
1708 char *value;
1709 UCTMultiThreadItem(const char *x) : value(NULL) {
1710 value = uprv_strdup(x);
1711 }
1712 virtual ~UCTMultiThreadItem() {
1713 uprv_free(value);
1714 }
1715 };
1716
1717 U_NAMESPACE_BEGIN
1718
1719 template<> U_EXPORT
1720 const UCTMultiThreadItem *LocaleCacheKey<UCTMultiThreadItem>::createObject(
1721 const void * /*unused*/, UErrorCode & /* status */) const {
1722 // Since multiple threads are hitting the cache for the first time,
1723 // no objects should be created yet.
1724 umtx_lock(&gCTMutex);
1725 if (gObjectsCreated != 0) {
1726 gThisTest->errln("Expected no objects to be created yet.");
1727 }
1728 umtx_unlock(&gCTMutex);
1729
1730 // Big, expensive object that takes 1 second to create.
1731 SimpleThread::sleep(1000);
1732
1733 // Log that we created an object.
1734 umtx_lock(&gCTMutex);
1735 ++gObjectsCreated;
1736 umtx_unlock(&gCTMutex);
1737 UCTMultiThreadItem *result = new UCTMultiThreadItem(fLoc.getName());
1738 result->addRef();
1739 return result;
1740 }
1741
1742 U_NAMESPACE_END
1743
1744 class UnifiedCacheThread: public SimpleThread {
1745 public:
1746 UnifiedCacheThread(const char *loc) : fLoc(loc) {};
1747 ~UnifiedCacheThread() {};
1748 void run();
1749 const char *fLoc;
1750 };
1751
1752 void UnifiedCacheThread::run() {
1753 UErrorCode status = U_ZERO_ERROR;
1754 const UnifiedCache *cache = UnifiedCache::getInstance(status);
1755 U_ASSERT(status == U_ZERO_ERROR);
1756 const UCTMultiThreadItem *item = NULL;
1757 cache->get(LocaleCacheKey<UCTMultiThreadItem>(fLoc), item, status);
1758 U_ASSERT(item != NULL);
1759 if (uprv_strcmp(fLoc, item->value)) {
1760 gThisTest->errln("Expected %s, got %s", fLoc, item->value);
1761 }
1762 item->removeRef();
1763
1764 // Mark this thread as finished
1765 umtx_lock(&gCTMutex);
1766 ++gFinishedThreads;
1767 umtx_condBroadcast(&gCTConditionVar);
1768 umtx_unlock(&gCTMutex);
1769 }
1770
1771 void MultithreadTest::TestUnifiedCache() {
1772 UErrorCode status = U_ZERO_ERROR;
1773 const UnifiedCache *cache = UnifiedCache::getInstance(status);
1774 U_ASSERT(cache != NULL);
1775 cache->flush();
1776 gThisTest = this;
1777 gFinishedThreads = 0;
1778 gObjectsCreated = 0;
1779
1780 UnifiedCacheThread *threads[CACHE_LOAD][UPRV_LENGTHOF(gCacheLocales)];
1781 for (int32_t i=0; i<CACHE_LOAD; ++i) {
1782 for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
1783 threads[i][j] = new UnifiedCacheThread(gCacheLocales[j]);
1784 threads[i][j]->start();
1785 }
1786 }
1787 // Wait on all the threads to complete verify that LENGTHOF(gCacheLocales)
1788 // objects were created.
1789 umtx_lock(&gCTMutex);
1790 while (gFinishedThreads < CACHE_LOAD*UPRV_LENGTHOF(gCacheLocales)) {
1791 umtx_condWait(&gCTConditionVar, &gCTMutex);
1792 }
1793 assertEquals("Objects created", UPRV_LENGTHOF(gCacheLocales), gObjectsCreated);
1794 umtx_unlock(&gCTMutex);
1795
1796 // clean up threads
1797 for (int32_t i=0; i<CACHE_LOAD; ++i) {
1798 for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
1799 delete threads[i][j];
1800 }
1801 }
1802 }
1803
1804 #endif // ICU_USE_THREADS