]> git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/tsmthred.cpp
ICU-6.2.4.tar.gz
[apple/icu.git] / icuSources / test / intltest / tsmthred.cpp
1 /********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 1999-2004, 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 "unicode/utypes.h"
14 #include "unicode/ustring.h"
15 #include "umutex.h"
16 #include "cmemory.h"
17 #include "cstring.h"
18 #include "uparse.h"
19 #include "unicode/resbund.h"
20 #include "unicode/udata.h"
21 #include "unicode/uloc.h"
22 #include "unicode/locid.h"
23 #include "putilimp.h"
24 #if !defined(WIN32) && !defined(XP_MAC) && !defined(U_RHAPSODY)
25 #define POSIX 1
26 #endif
27
28 #if defined(POSIX) || defined(U_SOLARIS) || defined(U_AIX) || defined(U_HPUX)
29
30 #define HAVE_IMP
31
32 #if (ICU_USE_THREADS == 1)
33 #include <pthread.h>
34 #endif
35
36 #if defined(__hpux) && defined(HPUX_CMA)
37 # if defined(read) // read being defined as cma_read causes trouble with iostream::read
38 # undef read
39 # endif
40 #endif
41
42 /* Define __EXTENSIONS__ for Solaris and old friends in strict mode. */
43 #ifndef __EXTENSIONS__
44 #define __EXTENSIONS__
45 #endif
46
47 #include <signal.h>
48
49 /* Define _XPG4_2 for Solaris and friends. */
50 #ifndef _XPG4_2
51 #define _XPG4_2
52 #endif
53
54 /* Define __USE_XOPEN_EXTENDED for Linux and glibc. */
55 #ifndef __USE_XOPEN_EXTENDED
56 #define __USE_XOPEN_EXTENDED
57 #endif
58
59 /* Define _INCLUDE_XOPEN_SOURCE_EXTENDED for HP/UX (11?). */
60 #ifndef _INCLUDE_XOPEN_SOURCE_EXTENDED
61 #define _INCLUDE_XOPEN_SOURCE_EXTENDED
62 #endif
63
64 #include <unistd.h>
65
66 #endif
67 /* HPUX */
68 #ifdef sleep
69 #undef sleep
70 #endif
71
72
73
74 #include "tsmthred.h"
75
76
77 MultithreadTest::MultithreadTest()
78 {
79 }
80
81 MultithreadTest::~MultithreadTest()
82 {
83 }
84
85
86
87 #if (ICU_USE_THREADS==0)
88 void MultithreadTest::runIndexedTest( int32_t index, UBool exec,
89 const char* &name, char* par ) {
90 if (exec) logln("TestSuite MultithreadTest: ");
91
92 if(index == 0)
93 name = "NO_THREADED_TESTS";
94 else
95 name = "";
96
97 if(exec) { logln("MultithreadTest - test DISABLED. ICU_USE_THREADS set to 0, check your configuration if this is a problem..");
98 }
99 }
100 #else
101
102
103
104 // Note: A LOT OF THE FUNCTIONS IN THIS FILE SHOULD LIVE ELSEWHERE!!!!!
105 // Note: A LOT OF THE FUNCTIONS IN THIS FILE SHOULD LIVE ELSEWHERE!!!!!
106 // -srl
107
108 #include <stdio.h>
109 #include <string.h>
110 #include <ctype.h> // tolower, toupper
111
112 #include "unicode/putil.h"
113
114 /* for mthreadtest*/
115 #include "unicode/numfmt.h"
116 #include "unicode/choicfmt.h"
117 #include "unicode/msgfmt.h"
118 #include "unicode/locid.h"
119 #include "unicode/ucol.h"
120 #include "unicode/calendar.h"
121 #include "ucaconf.h"
122
123 //-----------------------------------------------------------------------------------
124 //
125 // class SimpleThread Of course we need a thread class first..
126 // This wrapper has a ported implementation.
127 //
128 //-----------------------------------------------------------------------------------
129 class SimpleThread
130 {
131 public:
132 SimpleThread();
133 virtual ~SimpleThread();
134 int32_t start(void); // start the thread
135 UBool isRunning(); // return true if a started thread has exited.
136
137 virtual void run(void) = 0; // Override this to provide the code to run
138 // in the thread.
139 void *fImplementation;
140
141 public:
142 static void sleep(int32_t millis); // probably shouldn't go here but oh well.
143 static void errorFunc(); // Empty function, provides a single convenient place
144 // to break on errors.
145 };
146
147 void SimpleThread::errorFunc() {
148 // *(char *)0 = 3; // Force entry into a debugger via a crash;
149 }
150
151
152
153
154 #ifdef WIN32
155 #define HAVE_IMP
156
157 # define VC_EXTRALEAN
158 # define WIN32_LEAN_AND_MEAN
159 # define NOUSER
160 # define NOSERVICE
161 # define NOIME
162 # define NOMCX
163 #include <windows.h>
164 #include <process.h>
165
166
167
168 //-----------------------------------------------------------------------------------
169 //
170 // class SimpleThread Windows Implementation
171 //
172 //-----------------------------------------------------------------------------------
173 struct Win32ThreadImplementation
174 {
175 HANDLE fHandle;
176 unsigned int fThreadID;
177 };
178
179
180 extern "C" unsigned int __stdcall SimpleThreadProc(void *arg)
181 {
182 ((SimpleThread*)arg)->run();
183 return 0;
184 }
185
186 SimpleThread::SimpleThread()
187 :fImplementation(0)
188 {
189 Win32ThreadImplementation *imp = new Win32ThreadImplementation;
190 imp->fHandle = 0;
191 fImplementation = imp;
192 }
193
194 SimpleThread::~SimpleThread()
195 {
196 // Destructor. Because we start the thread running with _beginthreadex(),
197 // we own the Windows HANDLE for the thread and must
198 // close it here.
199 Win32ThreadImplementation *imp = (Win32ThreadImplementation*)fImplementation;
200 if (imp != 0) {
201 if (imp->fHandle != 0) {
202 CloseHandle(imp->fHandle);
203 imp->fHandle = 0;
204 }
205 }
206 delete (Win32ThreadImplementation*)fImplementation;
207 }
208
209 int32_t SimpleThread::start()
210 {
211 Win32ThreadImplementation *imp = (Win32ThreadImplementation*)fImplementation;
212 if(imp->fHandle != NULL) {
213 // The thread appears to have already been started.
214 // This is probably an error on the part of our caller.
215 return -1;
216 }
217
218 imp->fHandle = (HANDLE) _beginthreadex(
219 NULL, // Security
220 0x20000, // Stack Size
221 SimpleThreadProc, // Function to Run
222 (void *)this, // Arg List
223 0, // initflag. Start running, not suspended
224 &imp->fThreadID // thraddr
225 );
226
227 if (imp->fHandle == 0) {
228 // An error occured
229 int err = errno;
230 if (err == 0) {
231 err = -1;
232 }
233 return err;
234 }
235 return 0;
236 }
237
238
239 UBool SimpleThread::isRunning() {
240 //
241 // Test whether the thread associated with the SimpleThread object is
242 // still actually running.
243 //
244 // NOTE: on Win64 on Itanium processors, a crashes
245 // occur if the main thread of a process exits concurrently with some
246 // other thread(s) exiting. To avoid the possibility, we wait until the
247 // OS indicates that all threads have terminated, rather than waiting
248 // only until the end of the user's Run function has been reached.
249 //
250 // I don't know whether the crashes represent a Windows bug, or whether
251 // main() programs are supposed to have to wait for their threads.
252 //
253 Win32ThreadImplementation *imp = (Win32ThreadImplementation*)fImplementation;
254
255 bool success;
256 DWORD threadExitCode;
257
258 if (imp->fHandle == 0) {
259 // No handle, thread must not be running.
260 return FALSE;
261 }
262 success = GetExitCodeThread(imp->fHandle, &threadExitCode) != 0;
263 if (! success) {
264 // Can't get status, thread must not be running.
265 return FALSE;
266 }
267 return (threadExitCode == STILL_ACTIVE);
268 }
269
270
271 void SimpleThread::sleep(int32_t millis)
272 {
273 ::Sleep(millis);
274 }
275
276 //-----------------------------------------------------------------------------------
277 //
278 // class SimpleThread NULL Implementation
279 //
280 //-----------------------------------------------------------------------------------
281 #elif defined XP_MAC
282
283 // since the Mac has no preemptive threading (at least on MacOS 8), only
284 // cooperative threading, threads are a no-op. We have no yield() calls
285 // anywhere in the ICU, so we are guaranteed to be thread-safe.
286
287 #define HAVE_IMP
288
289 SimpleThread::SimpleThread()
290 {}
291
292 SimpleThread::~SimpleThread()
293 {}
294
295 int32_t
296 SimpleThread::start()
297 { return 0; }
298
299 void
300 SimpleThread::run()
301 {}
302
303 void
304 SimpleThread::sleep(int32_t millis)
305 {}
306
307 UBool
308 SimpleThread::isRunning() {
309 return FALSE;
310 }
311
312 #endif
313
314
315 //-----------------------------------------------------------------------------------
316 //
317 // class SimpleThread POSIX implementation
318 //
319 // A note on the POSIX vs the Windows implementations of this class..
320 // On Windows, the main thread must verify that other threads have finished
321 // before exiting, or crashes occasionally occur. (Seen on Itanium Win64 only)
322 // The function SimpleThread::isRunning() is used for this purpose.
323 //
324 // On POSIX, there is NO reliable non-blocking mechanism to determine
325 // whether a thread has exited. pthread_kill(thread, 0) almost works,
326 // but the system can recycle thread ids immediately, so seeing that a
327 // thread exists with this call could mean that the original thread has
328 // finished and a new one started with the same ID. Useless.
329 //
330 // So we need to do the check with user code, by setting a flag just before
331 // the thread function returns. A technique that is guaranteed to fail
332 // on Windows, because it indicates that the thread is done before all
333 // system level cleanup has happened.
334 //
335 //-----------------------------------------------------------------------------------
336 #if defined(POSIX)||defined(U_SOLARIS)||defined(U_AIX)||defined(U_HPUX)
337 #define HAVE_IMP
338
339 struct PosixThreadImplementation
340 {
341 pthread_t fThread;
342 UBool fRunning;
343 UBool fRan; /* True if the thread was successfully started */
344 };
345
346 extern "C" void* SimpleThreadProc(void *arg)
347 {
348 // This is the code that is run in the new separate thread.
349 SimpleThread *This = (SimpleThread *)arg;
350 This->run(); // Run the user code.
351
352 // The user function has returned. Set the flag indicating that this thread
353 // is done. Need a mutex for memory barrier purposes only, so that other thread
354 // will reliably see that the flag has changed.
355 PosixThreadImplementation *imp = (PosixThreadImplementation*)This->fImplementation;
356 umtx_lock(NULL);
357 imp->fRunning = FALSE;
358 umtx_unlock(NULL);
359 return 0;
360 }
361
362 SimpleThread::SimpleThread()
363 {
364 PosixThreadImplementation *imp = new PosixThreadImplementation;
365 imp->fRunning = FALSE;
366 imp->fRan = FALSE;
367 fImplementation = imp;
368 }
369
370 SimpleThread::~SimpleThread()
371 {
372 PosixThreadImplementation *imp = (PosixThreadImplementation*)fImplementation;
373 if (imp->fRan) {
374 pthread_join(imp->fThread, NULL);
375 }
376 delete imp;
377 fImplementation = (void *)0xdeadbeef;
378 }
379
380 int32_t SimpleThread::start()
381 {
382 int32_t rc;
383 static pthread_attr_t attr;
384 static UBool attrIsInitialized = FALSE;
385
386 PosixThreadImplementation *imp = (PosixThreadImplementation*)fImplementation;
387 imp->fRunning = TRUE;
388 imp->fRan = TRUE;
389
390 #ifdef HPUX_CMA
391 if (attrIsInitialized == FALSE) {
392 rc = pthread_attr_create(&attr);
393 attrIsInitialized = TRUE;
394 }
395 rc = pthread_create(&(imp->fThread),attr,&SimpleThreadProc,(void*)this);
396 #else
397 if (attrIsInitialized == FALSE) {
398 rc = pthread_attr_init(&attr);
399 #if defined(OS390)
400 {
401 int detachstate = 0; /* jdc30: detach state of zero causes
402 threads created with this attr to be in
403 an undetached state. An undetached
404 thread will keep its resources after
405 termination. */
406 pthread_attr_setdetachstate(&attr, &detachstate);
407 }
408 #else
409 // pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
410 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
411 #endif
412 attrIsInitialized = TRUE;
413 }
414 rc = pthread_create(&(imp->fThread),&attr,&SimpleThreadProc,(void*)this);
415 #endif
416
417 if (rc != 0) {
418 // some kind of error occured, the thread did not start.
419 imp->fRan = FALSE;
420 imp->fRunning = FALSE;
421 }
422
423 return rc;
424 }
425
426
427 UBool
428 SimpleThread::isRunning() {
429 // Note: Mutex functions are used here not for synchronization,
430 // but to force memory barriors to exist, to ensure that one thread
431 // can see changes made by another when running on processors
432 // with memory models having weak coherency.
433 PosixThreadImplementation *imp = (PosixThreadImplementation*)fImplementation;
434 umtx_lock(NULL);
435 UBool retVal = imp->fRunning;
436 umtx_unlock(NULL);
437 return retVal;
438 }
439
440
441 void SimpleThread::sleep(int32_t millis)
442 {
443 #ifdef U_SOLARIS
444 sigignore(SIGALRM);
445 #endif
446
447 #ifdef HPUX_CMA
448 cma_sleep(millis/100);
449 #elif defined(U_HPUX) || defined(OS390)
450 millis *= 1000;
451 while(millis >= 1000000) {
452 usleep(999999);
453 millis -= 1000000;
454 }
455 if(millis > 0) {
456 usleep(millis);
457 }
458 #else
459 usleep(millis * 1000);
460 #endif
461 }
462
463 #endif
464 // end POSIX
465
466
467 #ifndef HAVE_IMP
468 #error No implementation for threads! Cannot test.
469 0 = 216; //die
470 #endif
471
472
473 // *************** end fluff ******************
474
475 /* now begins the real test. */
476 void MultithreadTest::runIndexedTest( int32_t index, UBool exec,
477 const char* &name, char* /*par*/ ) {
478 if (exec)
479 logln("TestSuite MultithreadTest: ");
480 switch (index) {
481 case 0:
482 name = "TestThreads";
483 if (exec)
484 TestThreads();
485 break;
486
487 case 1:
488 name = "TestMutex";
489 if (exec)
490 TestMutex();
491 break;
492
493 case 2:
494 name = "TestThreadedIntl";
495 #if !UCONFIG_NO_FORMATTING
496 if (exec) {
497 TestThreadedIntl();
498 }
499 #endif
500 break;
501
502 case 3:
503 name = "TestCollators";
504 #if !UCONFIG_NO_COLLATION
505 if (exec) {
506 TestCollators();
507 }
508 #endif /* #if !UCONFIG_NO_COLLATION */
509 break;
510
511 case 4:
512 name = "TestString";
513 if (exec) {
514 TestString();
515 }
516 break;
517
518 default:
519 name = "";
520 break; //needed to end loop
521 }
522 }
523
524
525 //-----------------------------------------------------------------------------------
526 //
527 // TestThreads -- see if threads really work at all.
528 //
529 // Set up N threads pointing at N chars. When they are started, they will
530 // each sleep 1 second and then set their chars. At the end we make sure they
531 // are all set.
532 //
533 //-----------------------------------------------------------------------------------
534 #define THREADTEST_NRTHREADS 8
535
536 class TestThreadsThread : public SimpleThread
537 {
538 public:
539 TestThreadsThread(char* whatToChange) { fWhatToChange = whatToChange; }
540 virtual void run() { SimpleThread::sleep(1000);
541 Mutex m;
542 *fWhatToChange = '*';
543 }
544 private:
545 char *fWhatToChange;
546 };
547
548 void MultithreadTest::TestThreads()
549 {
550 char threadTestChars[THREADTEST_NRTHREADS + 1];
551 SimpleThread *threads[THREADTEST_NRTHREADS];
552
553 int32_t i;
554 for(i=0;i<THREADTEST_NRTHREADS;i++)
555 {
556 threadTestChars[i] = ' ';
557 threads[i] = new TestThreadsThread(&threadTestChars[i]);
558 }
559 threadTestChars[THREADTEST_NRTHREADS] = '\0';
560
561 logln("->" + UnicodeString(threadTestChars) + "<- Firing off threads.. ");
562 for(i=0;i<THREADTEST_NRTHREADS;i++)
563 {
564 if (threads[i]->start() != 0) {
565 errln("Error starting thread %d", i);
566 }
567 SimpleThread::sleep(200);
568 logln(" Subthread started.");
569 }
570
571 logln("Waiting for threads to be set..");
572
573 int32_t patience = 40; // seconds to wait
574
575 while(patience--)
576 {
577 int32_t count = 0;
578 umtx_lock(NULL);
579 for(i=0;i<THREADTEST_NRTHREADS;i++)
580 {
581 if(threadTestChars[i] == '*')
582 {
583 count++;
584 }
585 }
586 umtx_unlock(NULL);
587
588 if(count == THREADTEST_NRTHREADS)
589 {
590 logln("->" + UnicodeString(threadTestChars) + "<- Got all threads! cya");
591 for(i=0;i<THREADTEST_NRTHREADS;i++)
592 {
593 delete threads[i];
594 }
595 return;
596 }
597
598 logln("->" + UnicodeString(threadTestChars) + "<- Waiting..");
599 SimpleThread::sleep(500);
600 }
601
602 errln("->" + UnicodeString(threadTestChars) + "<- PATIENCE EXCEEDED!! Still missing some.");
603 for(i=0;i<THREADTEST_NRTHREADS;i++)
604 {
605 delete threads[i];
606 }
607 }
608
609
610 class TestMutexThread1 : public SimpleThread
611 {
612 public:
613 TestMutexThread1() : fDone(FALSE) {}
614 virtual void run()
615 {
616 Mutex m; // grab the lock first thing
617 SimpleThread::sleep(900); // then wait
618 fDone = TRUE; // finally, set our flag
619 }
620 public:
621 UBool fDone;
622 };
623
624 class TestMutexThread2 : public SimpleThread
625 {
626 public:
627 TestMutexThread2(TestMutexThread1& r) : fOtherThread(r), fDone(FALSE), fErr(FALSE) {}
628 virtual void run()
629 {
630 SimpleThread::sleep(500); // wait, make sure they aquire the lock
631 fElapsed = uprv_getUTCtime();
632 {
633 Mutex m; // wait here
634
635 fElapsed = uprv_getUTCtime() - fElapsed;
636
637 if(fOtherThread.fDone == FALSE)
638 fErr = TRUE; // they didnt get to it yet
639
640 fDone = TRUE; // we're done.
641 }
642 }
643 public:
644 TestMutexThread1 & fOtherThread;
645 UBool fDone, fErr;
646 UDate fElapsed;
647 private:
648 /**
649 * The assignment operator has no real implementation.
650 * It is provided to make the compiler happy. Do not call.
651 */
652 TestMutexThread2& operator=(const TestMutexThread2&) { return *this; }
653 };
654
655 void MultithreadTest::TestMutex()
656 {
657 /* this test uses printf so that we don't hang by calling UnicodeString inside of a mutex. */
658 //logln("Bye.");
659 // printf("Warning: MultiThreadTest::Testmutex() disabled.\n");
660 // return;
661
662 if(verbose)
663 printf("Before mutex.\n");
664 {
665 Mutex m;
666 if(verbose)
667 printf(" Exited 2nd mutex\n");
668 }
669 if(verbose)
670 printf("exited 1st mutex. Now testing with threads:");
671
672 TestMutexThread1 thread1;
673 TestMutexThread2 thread2(thread1);
674 if (thread2.start() != 0 ||
675 thread1.start() != 0 ) {
676 errln("Error starting threads.");
677 }
678
679 for(int32_t patience = 12; patience > 0;patience--)
680 {
681 // TODO: Possible memory coherence issue in looking at fDone values
682 // that are set in another thread without the mutex here.
683 if(thread1.fDone && verbose)
684 printf("Thread1 done\n");
685
686 if(thread1.fDone && thread2.fDone)
687 {
688 if(thread2.fErr)
689 errln("Thread 2 says: thread1 didn't run before I aquired the mutex.");
690 logln("took %lu seconds for thread2 to aquire the mutex.", (int)(thread2.fElapsed/U_MILLIS_PER_DAY));
691 return;
692 }
693 SimpleThread::sleep(1000);
694 }
695 if(verbose)
696 printf("patience exceeded. [WARNING mutex may still be acquired.] ");
697 }
698
699
700
701
702 //-------------------------------------------------------------------------------------------
703 //
704 // class ThreadWithStatus - a thread that we can check the status and error condition of
705 //
706 //-------------------------------------------------------------------------------------------
707 class ThreadWithStatus : public SimpleThread
708 {
709 public:
710 UBool getError() { return (fErrors > 0); }
711 UBool getError(UnicodeString& fillinError) { fillinError = fErrorString; return (fErrors > 0); }
712 virtual ~ThreadWithStatus(){}
713 protected:
714 ThreadWithStatus() : fErrors(0) {}
715 void error(const UnicodeString &error) {
716 fErrors++; fErrorString = error;
717 SimpleThread::errorFunc();
718 }
719 void error() { error("An error occured."); }
720 private:
721 int32_t fErrors;
722 UnicodeString fErrorString;
723 };
724
725
726
727 //-------------------------------------------------------------------------------------------
728 //
729 // TestMultithreadedIntl. Test ICU Formatting n a multi-threaded environment
730 //
731 //-------------------------------------------------------------------------------------------
732
733
734 // * Show exactly where the string's differences lie.
735 UnicodeString showDifference(const UnicodeString& expected, const UnicodeString& result)
736 {
737 UnicodeString res;
738 res = expected + "<Expected\n";
739 if(expected.length() != result.length())
740 res += " [ Different lengths ] \n";
741 else
742 {
743 for(int32_t i=0;i<expected.length();i++)
744 {
745 if(expected[i] == result[i])
746 {
747 res += " ";
748 }
749 else
750 {
751 res += "|";
752 }
753 }
754 res += "<Differences";
755 res += "\n";
756 }
757 res += result + "<Result\n";
758
759 return res;
760 }
761
762
763
764
765 //-------------------------------------------------------------------------------------------
766 //
767 // FormatThreadTest - a thread that tests performing a number of numberformats.
768 //
769 //-------------------------------------------------------------------------------------------
770
771 const int kFormatThreadIterations = 20; // # of iterations per thread
772 const int kFormatThreadThreads = 10; // # of threads to spawn
773 const int kFormatThreadPatience = 60; // time in seconds to wait for all threads
774
775 #if !UCONFIG_NO_FORMATTING
776
777
778
779 struct FormatThreadTestData
780 {
781 double number;
782 UnicodeString string;
783 FormatThreadTestData(double a, const UnicodeString& b) : number(a),string(b) {}
784 } ;
785
786
787 void errorToString(UErrorCode theStatus, UnicodeString &string)
788 {
789 string=u_errorName(theStatus);
790 }
791
792 // "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3 number,currency}."
793
794 void formatErrorMessage(UErrorCode &realStatus, const UnicodeString& pattern, const Locale& theLocale,
795 UErrorCode inStatus0, /* statusString 1 */ const Locale &inCountry2, double currency3, // these numbers are the message arguments.
796 UnicodeString &result)
797 {
798 if(U_FAILURE(realStatus))
799 return; // you messed up
800
801 UnicodeString errString1;
802 errorToString(inStatus0, errString1);
803
804 UnicodeString countryName2;
805 inCountry2.getDisplayCountry(theLocale,countryName2);
806
807 Formattable myArgs[] = {
808 Formattable((int32_t)inStatus0), // inStatus0 {0}
809 Formattable(errString1), // statusString1 {1}
810 Formattable(countryName2), // inCountry2 {2}
811 Formattable(currency3)// currency3 {3,number,currency}
812 };
813
814 MessageFormat *fmt = new MessageFormat("MessageFormat's API is broken!!!!!!!!!!!",realStatus);
815 fmt->setLocale(theLocale);
816 fmt->applyPattern(pattern, realStatus);
817
818 if (U_FAILURE(realStatus)) {
819 delete fmt;
820 return;
821 }
822
823 FieldPosition ignore = 0;
824 fmt->format(myArgs,4,result,ignore,realStatus);
825
826 delete fmt;
827 };
828
829
830 UBool U_CALLCONV isAcceptable(void *, const char *, const char *, const UDataInfo *) {
831 return TRUE;
832 }
833
834 //static UMTX debugMutex = NULL;
835 //static UMTX gDebugMutex;
836
837
838 class FormatThreadTest : public ThreadWithStatus
839 {
840 public:
841 int fNum;
842 int fTraceInfo;
843
844 FormatThreadTest() // constructor is NOT multithread safe.
845 : ThreadWithStatus(),
846 fNum(0),
847 fTraceInfo(0),
848 fOffset(0)
849 // the locale to use
850 {
851 static int32_t fgOffset = 0;
852 fgOffset += 3;
853 fOffset = fgOffset;
854 }
855
856
857 virtual void run()
858 {
859 fTraceInfo = 1;
860 NumberFormat *formatter = NULL;
861 NumberFormat *percentFormatter = NULL;
862 UErrorCode status = U_ZERO_ERROR;
863
864 #if 0
865 // debugging code,
866 for (int i=0; i<4000; i++) {
867 status = U_ZERO_ERROR;
868 UDataMemory *data1 = udata_openChoice(0, "res", "en_US", isAcceptable, 0, &status);
869 UDataMemory *data2 = udata_openChoice(0, "res", "fr", isAcceptable, 0, &status);
870 udata_close(data1);
871 udata_close(data2);
872 if (U_FAILURE(status)) {
873 error("udata_openChoice failed.\n");
874 break;
875 }
876 }
877 return;
878 #endif
879
880 #if 1
881 // debugging code,
882 int m;
883 for (m=0; m<4000; m++) {
884 status = U_ZERO_ERROR;
885 UResourceBundle *res = NULL;
886 const char *localeName = NULL;
887
888 Locale loc = Locale::getEnglish();
889
890 localeName = loc.getName();
891 // localeName = "en";
892
893 // ResourceBundle bund = ResourceBundle(0, loc, status);
894 //umtx_lock(&gDebugMutex);
895 res = ures_open(NULL, localeName, &status);
896 //umtx_unlock(&gDebugMutex);
897
898 //umtx_lock(&gDebugMutex);
899 ures_close(res);
900 //umtx_unlock(&gDebugMutex);
901
902 if (U_FAILURE(status)) {
903 error("Resource bundle construction failed.\n");
904 break;
905 }
906 }
907 return;
908 #endif
909
910 // Keep this data here to avoid static initialization.
911 FormatThreadTestData kNumberFormatTestData[] =
912 {
913 FormatThreadTestData((double)5.0, UnicodeString("5", "")),
914 FormatThreadTestData( 6.0, UnicodeString("6", "")),
915 FormatThreadTestData( 20.0, UnicodeString("20", "")),
916 FormatThreadTestData( 8.0, UnicodeString("8", "")),
917 FormatThreadTestData( 8.3, UnicodeString("8.3", "")),
918 FormatThreadTestData( 12345, UnicodeString("12,345", "")),
919 FormatThreadTestData( 81890.23, UnicodeString("81,890.23", "")),
920 };
921 int32_t kNumberFormatTestDataLength = (int32_t)(sizeof(kNumberFormatTestData) /
922 sizeof(kNumberFormatTestData[0]));
923
924 // Keep this data here to avoid static initialization.
925 FormatThreadTestData kPercentFormatTestData[] =
926 {
927 FormatThreadTestData((double)5.0, UnicodeString("500%", "")),
928 FormatThreadTestData( 1.0, UnicodeString("100%", "")),
929 FormatThreadTestData( 0.26, UnicodeString("26%", "")),
930 FormatThreadTestData(
931 16384.99, CharsToUnicodeString("1\\u00a0638\\u00a0499%") ), // U+00a0 = NBSP
932 FormatThreadTestData(
933 81890.23, CharsToUnicodeString("8\\u00a0189\\u00a0023%" )),
934 };
935 int32_t kPercentFormatTestDataLength =
936 (int32_t)(sizeof(kPercentFormatTestData) / sizeof(kPercentFormatTestData[0]));
937 int32_t iteration;
938
939 status = U_ZERO_ERROR;
940 formatter = NumberFormat::createInstance(Locale::getEnglish(),status);
941 if(U_FAILURE(status)) {
942 error("Error on NumberFormat::createInstance()");
943 goto cleanupAndReturn;
944 }
945
946 percentFormatter = NumberFormat::createPercentInstance(Locale::getFrench(),status);
947 if(U_FAILURE(status)) {
948 error("Error on NumberFormat::createPercentInstance()");
949 goto cleanupAndReturn;
950 }
951
952 for(iteration = 0;!getError() && iteration<kFormatThreadIterations;iteration++)
953 {
954
955 int32_t whichLine = (iteration + fOffset)%kNumberFormatTestDataLength;
956
957 UnicodeString output;
958
959 formatter->format(kNumberFormatTestData[whichLine].number, output);
960
961 if(0 != output.compare(kNumberFormatTestData[whichLine].string)) {
962 error("format().. expected " + kNumberFormatTestData[whichLine].string
963 + " got " + output);
964 goto cleanupAndReturn;
965 }
966
967 // Now check percent.
968 output.remove();
969 whichLine = (iteration + fOffset)%kPercentFormatTestDataLength;
970
971 percentFormatter->format(kPercentFormatTestData[whichLine].number, output);
972 if(0 != output.compare(kPercentFormatTestData[whichLine].string))
973 {
974 error("percent format().. \n" +
975 showDifference(kPercentFormatTestData[whichLine].string,output));
976 goto cleanupAndReturn;
977 }
978
979 // Test message error
980 const int kNumberOfMessageTests = 3;
981 UErrorCode statusToCheck;
982 UnicodeString patternToCheck;
983 Locale messageLocale;
984 Locale countryToCheck;
985 double currencyToCheck;
986
987 UnicodeString expected;
988
989 // load the cases.
990 switch((iteration+fOffset) % kNumberOfMessageTests)
991 {
992 default:
993 case 0:
994 statusToCheck= U_FILE_ACCESS_ERROR;
995 patternToCheck= "0:Someone from {2} is receiving a #{0}"
996 " error - {1}. Their telephone call is costing "
997 "{3,number,currency}."; // number,currency
998 messageLocale= Locale("en","US");
999 countryToCheck= Locale("","HR");
1000 currencyToCheck= 8192.77;
1001 expected= "0:Someone from Croatia is receiving a #4 error - "
1002 "U_FILE_ACCESS_ERROR. Their telephone call is costing $8,192.77.";
1003 break;
1004 case 1:
1005 statusToCheck= U_INDEX_OUTOFBOUNDS_ERROR;
1006 patternToCheck= "1:A customer in {2} is receiving a #{0} error - {1}. Their telephone call is costing {3,number,currency}."; // number,currency
1007 messageLocale= Locale("de","DE@currency=DEM");
1008 countryToCheck= Locale("","BF");
1009 currencyToCheck= 2.32;
1010 expected= "1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. Their telephone call is costing 2,32 DM.";
1011 break;
1012 case 2:
1013 statusToCheck= U_MEMORY_ALLOCATION_ERROR;
1014 patternToCheck= "2:user in {2} is receiving a #{0} error - {1}. "
1015 "They insist they just spent {3,number,currency} "
1016 "on memory."; // number,currency
1017 messageLocale= Locale("de","AT@currency=ATS"); // Austrian German
1018 countryToCheck= Locale("","US"); // hmm
1019 currencyToCheck= 40193.12;
1020 expected= CharsToUnicodeString(
1021 "2:user in Vereinigte Staaten is receiving a #7 error"
1022 " - U_MEMORY_ALLOCATION_ERROR. They insist they just spent"
1023 " \\u00f6S 40.193,12 on memory.");
1024 break;
1025 }
1026
1027 UnicodeString result;
1028 UErrorCode status = U_ZERO_ERROR;
1029 formatErrorMessage(status,patternToCheck,messageLocale,statusToCheck,
1030 countryToCheck,currencyToCheck,result);
1031 if(U_FAILURE(status))
1032 {
1033 UnicodeString tmp;
1034 errorToString(status,tmp);
1035 error("Failure on message format, pattern=" + patternToCheck +
1036 ", error = " + tmp);
1037 goto cleanupAndReturn;
1038 }
1039
1040 if(result != expected)
1041 {
1042 error("PatternFormat: \n" + showDifference(expected,result));
1043 goto cleanupAndReturn;
1044 }
1045 } /* end of for loop */
1046
1047 cleanupAndReturn:
1048 delete formatter;
1049 delete percentFormatter;
1050
1051 // while (fNum == 4) {SimpleThread::sleep(10000);} // Force a failure by preventing thread from finishing
1052 fTraceInfo = 2;
1053 }
1054
1055 private:
1056 int32_t fOffset; // where we are testing from.
1057 };
1058
1059 // ** The actual test function.
1060
1061 void MultithreadTest::TestThreadedIntl()
1062 {
1063 int i;
1064 UnicodeString theErr;
1065 UBool haveDisplayedInfo[kFormatThreadThreads];
1066
1067 //
1068 // Create and start the test threads
1069 //
1070 logln("Spawning: %d threads * %d iterations each.",
1071 kFormatThreadThreads, kFormatThreadIterations);
1072 FormatThreadTest *tests = new FormatThreadTest[kFormatThreadThreads];
1073 for(int32_t j = 0; j < kFormatThreadThreads; j++) {
1074 tests[j].fNum = j;
1075 int32_t threadStatus = tests[j].start();
1076 if (threadStatus != 0) {
1077 errln("System Error %d starting thread number %d.", threadStatus, j);
1078 SimpleThread::errorFunc();
1079 goto cleanupAndReturn;
1080 }
1081 haveDisplayedInfo[j] = FALSE;
1082 }
1083
1084
1085 // Spin, waiting for the test threads to finish.
1086 // (An earlier version used a wait in this loop, but that seems to trigger
1087 // a bug in some versions of AIX.)
1088 UBool stillRunning;
1089 do {
1090 /* Spin until the test threads complete. */
1091 stillRunning = FALSE;
1092 for(i=0;i<kFormatThreadThreads;i++) {
1093 if (tests[i].isRunning()) {
1094 stillRunning = TRUE;
1095 } else if (haveDisplayedInfo[i] == FALSE) {
1096 logln("Thread # %d is complete..", i);
1097 if(tests[i].getError(theErr)) {
1098 errln(UnicodeString("#") + i + ": " + theErr);
1099 SimpleThread::errorFunc();
1100 }
1101 haveDisplayedInfo[i] = TRUE;
1102 }
1103 }
1104 } while (stillRunning);
1105
1106 //
1107 // All threads have finished.
1108 //
1109 cleanupAndReturn:
1110 delete [] tests;
1111 return;
1112
1113 }
1114 #endif /* #if !UCONFIG_NO_FORMATTING */
1115
1116
1117
1118
1119
1120 //-------------------------------------------------------------------------------------------
1121 //
1122 // Collation threading test
1123 //
1124 //-------------------------------------------------------------------------------------------
1125 #if !UCONFIG_NO_COLLATION
1126
1127 #define kCollatorThreadThreads 10 // # of threads to spawn
1128 #define kCollatorThreadPatience kCollatorThreadThreads*100
1129
1130 struct Line {
1131 UChar buff[25];
1132 int32_t buflen;
1133 } ;
1134
1135 class CollatorThreadTest : public ThreadWithStatus
1136 {
1137 private:
1138 const UCollator *coll;
1139 const Line *lines;
1140 int32_t noLines;
1141 public:
1142 CollatorThreadTest() : ThreadWithStatus(),
1143 coll(NULL),
1144 lines(NULL),
1145 noLines(0)
1146 {
1147 };
1148 void setCollator(UCollator *c, Line *l, int32_t nl)
1149 {
1150 coll = c;
1151 lines = l;
1152 noLines = nl;
1153 }
1154 virtual void run() {
1155 //sleep(10000);
1156 int32_t line = 0;
1157
1158 uint8_t sk1[1024], sk2[1024];
1159 uint8_t *oldSk = NULL, *newSk = sk1;
1160 int32_t resLen = 0, oldLen = 0;
1161 int32_t i = 0;
1162
1163 for(i = 0; i < noLines; i++) {
1164 resLen = ucol_getSortKey(coll, lines[i].buff, lines[i].buflen, newSk, 1024);
1165
1166 int32_t res = 0, cmpres = 0, cmpres2 = 0;
1167
1168 if(oldSk != NULL) {
1169 res = strcmp((char *)oldSk, (char *)newSk);
1170 cmpres = ucol_strcoll(coll, lines[i-1].buff, lines[i-1].buflen, lines[i].buff, lines[i].buflen);
1171 cmpres2 = ucol_strcoll(coll, lines[i].buff, lines[i].buflen, lines[i-1].buff, lines[i-1].buflen);
1172 //cmpres = res;
1173 //cmpres2 = -cmpres;
1174
1175 if(cmpres != -cmpres2) {
1176 error("Compare result not symmetrical on line "+ line);
1177 break;
1178 }
1179
1180 if(((res&0x80000000) != (cmpres&0x80000000)) || (res == 0 && cmpres != 0) || (res != 0 && cmpres == 0)) {
1181 error(UnicodeString("Difference between ucol_strcoll and sortkey compare on line ")+ UnicodeString(line));
1182 break;
1183 }
1184
1185 if(res > 0) {
1186 error(UnicodeString("Line %i is not greater or equal than previous line ")+ UnicodeString(i));
1187 break;
1188 } else if(res == 0) { /* equal */
1189 res = u_strcmpCodePointOrder(lines[i-1].buff, lines[i].buff);
1190 if (res == 0) {
1191 error(UnicodeString("Probable error in test file on line %i (comparing identical strings)")+ UnicodeString(i));
1192 break;
1193 } else if (res > 0) {
1194 error(UnicodeString("Sortkeys are identical, but code point comapare gives >0 on line ")+ UnicodeString(i));
1195 break;
1196 }
1197 }
1198 }
1199
1200 oldSk = newSk;
1201 oldLen = resLen;
1202
1203 newSk = (newSk == sk1)?sk2:sk1;
1204 }
1205 }
1206
1207 };
1208
1209 void MultithreadTest::TestCollators()
1210 {
1211
1212 UErrorCode status = U_ZERO_ERROR;
1213 FILE *testFile = NULL;
1214 char testDataPath[1024];
1215 strcpy(testDataPath, IntlTest::getSourceTestData(status));
1216 if (U_FAILURE(status)) {
1217 errln("ERROR: could not open test data %s", u_errorName(status));
1218 return;
1219 }
1220 strcat(testDataPath, "CollationTest_");
1221
1222 const char* type = "NON_IGNORABLE";
1223
1224 const char *ext = ".txt";
1225 if(testFile) {
1226 fclose(testFile);
1227 }
1228 char buffer[1024];
1229 strcpy(buffer, testDataPath);
1230 strcat(buffer, type);
1231 size_t bufLen = strlen(buffer);
1232
1233 // we try to open 3 files:
1234 // path/CollationTest_type.txt
1235 // path/CollationTest_type_SHORT.txt
1236 // path/CollationTest_type_STUB.txt
1237 // we are going to test with the first one that we manage to open.
1238
1239 strcpy(buffer+bufLen, ext);
1240
1241 testFile = fopen(buffer, "rb");
1242
1243 if(testFile == 0) {
1244 strcpy(buffer+bufLen, "_SHORT");
1245 strcat(buffer, ext);
1246 testFile = fopen(buffer, "rb");
1247
1248 if(testFile == 0) {
1249 strcpy(buffer+bufLen, "_STUB");
1250 strcat(buffer, ext);
1251 testFile = fopen(buffer, "rb");
1252
1253 if (testFile == 0) {
1254 *(buffer+bufLen) = 0;
1255 errln("ERROR: could not open any of the conformance test files, tried opening base %s", buffer);
1256 return;
1257 } else {
1258 infoln(
1259 "INFO: Working with the stub file.\n"
1260 "If you need the full conformance test, please\n"
1261 "download the appropriate data files from:\n"
1262 "http://oss.software.ibm.com/cvs/icu4j/unicodetools/com/ibm/text/data/");
1263 }
1264 }
1265 }
1266
1267 Line *lines = new Line[200000];
1268 memset(lines, 0, sizeof(Line)*200000);
1269 int32_t lineNum = 0;
1270
1271 UChar bufferU[1024];
1272 int32_t buflen = 0;
1273 uint32_t first = 0;
1274 uint32_t offset = 0;
1275
1276 while (fgets(buffer, 1024, testFile) != NULL) {
1277 offset = 0;
1278 if(*buffer == 0 || buffer[0] == '#') {
1279 continue;
1280 }
1281 offset = u_parseString(buffer, bufferU, 1024, &first, &status);
1282 buflen = offset;
1283 bufferU[offset++] = 0;
1284 lines[lineNum].buflen = buflen;
1285 //lines[lineNum].buff = new UChar[buflen+1];
1286 u_memcpy(lines[lineNum].buff, bufferU, buflen);
1287 lineNum++;
1288 }
1289 fclose(testFile);
1290
1291
1292
1293 UCollator *coll = ucol_open("root", &status);
1294 if(U_FAILURE(status)) {
1295 errln("Couldn't open UCA collator");
1296 return;
1297 }
1298 ucol_setAttribute(coll, UCOL_NORMALIZATION_MODE, UCOL_ON, &status);
1299 ucol_setAttribute(coll, UCOL_CASE_FIRST, UCOL_OFF, &status);
1300 ucol_setAttribute(coll, UCOL_CASE_LEVEL, UCOL_OFF, &status);
1301 ucol_setAttribute(coll, UCOL_STRENGTH, UCOL_TERTIARY, &status);
1302 ucol_setAttribute(coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE, &status);
1303
1304 int32_t noSpawned = 0;
1305 int32_t spawnResult = 0;
1306 CollatorThreadTest *tests;
1307 tests = new CollatorThreadTest[kCollatorThreadThreads];
1308
1309 logln(UnicodeString("Spawning: ") + kCollatorThreadThreads + " threads * " + kFormatThreadIterations + " iterations each.");
1310 int32_t j = 0;
1311 for(j = 0; j < kCollatorThreadThreads; j++) {
1312 //logln("Setting collator %i", j);
1313 tests[j].setCollator(coll, lines, lineNum);
1314 }
1315 for(j = 0; j < kCollatorThreadThreads; j++) {
1316 log("%i ", j);
1317 spawnResult = tests[j].start();
1318 if(spawnResult != 0) {
1319 infoln("THREAD INFO: Couldn't spawn more than %i threads", noSpawned);
1320 break;
1321 }
1322 noSpawned++;
1323 }
1324 logln("Spawned all");
1325
1326 //for(int32_t patience = kCollatorThreadPatience;patience > 0; patience --)
1327 for(;;)
1328 {
1329 logln("Waiting...");
1330
1331 int32_t i;
1332 int32_t terrs = 0;
1333 int32_t completed =0;
1334
1335 for(i=0;i<kCollatorThreadThreads;i++)
1336 {
1337 if (tests[i].isRunning() == FALSE)
1338 {
1339 completed++;
1340
1341 //logln(UnicodeString("Test #") + i + " is complete.. ");
1342
1343 UnicodeString theErr;
1344 if(tests[i].getError(theErr))
1345 {
1346 terrs++;
1347 errln(UnicodeString("#") + i + ": " + theErr);
1348 }
1349 // print out the error, too, if any.
1350 }
1351 }
1352 logln("Completed %i tests", completed);
1353
1354 if(completed == noSpawned)
1355 {
1356 logln("Done! All %i tests are finished", noSpawned);
1357
1358 if(terrs)
1359 {
1360 errln("There were errors.");
1361 SimpleThread::errorFunc();
1362 }
1363 ucol_close(coll);
1364 delete[] tests;
1365 //for(i = 0; i < lineNum; i++) {
1366 //delete[] lines[i].buff;
1367 //}
1368 delete[] lines;
1369
1370 return;
1371 }
1372
1373 SimpleThread::sleep(900);
1374 }
1375 errln("patience exceeded. ");
1376 SimpleThread::errorFunc();
1377 ucol_close(coll);
1378 }
1379
1380 #endif /* #if !UCONFIG_NO_COLLATION */
1381
1382
1383
1384
1385 //-------------------------------------------------------------------------------------------
1386 //
1387 // StringThreadTest2
1388 //
1389 //-------------------------------------------------------------------------------------------
1390
1391 const int kStringThreadIterations = 2500;// # of iterations per thread
1392 const int kStringThreadThreads = 10; // # of threads to spawn
1393 const int kStringThreadPatience = 120; // time in seconds to wait for all threads
1394
1395
1396 class StringThreadTest2 : public ThreadWithStatus
1397 {
1398 public:
1399 int fNum;
1400 int fTraceInfo;
1401 const UnicodeString *fSharedString;
1402
1403 StringThreadTest2(const UnicodeString *sharedString, int num) // constructor is NOT multithread safe.
1404 : ThreadWithStatus(),
1405 fNum(num),
1406 fTraceInfo(0),
1407 fSharedString(sharedString)
1408 {
1409 };
1410
1411
1412 virtual void run()
1413 {
1414 fTraceInfo = 1;
1415 int loopCount = 0;
1416
1417 for (loopCount = 0; loopCount < kStringThreadIterations; loopCount++) {
1418 if (*fSharedString != "This is the original test string.") {
1419 error("Original string is corrupt.");
1420 break;
1421 }
1422 UnicodeString s1 = *fSharedString;
1423 s1 += "cat this";
1424 UnicodeString s2(s1);
1425 UnicodeString s3 = *fSharedString;
1426 s2 = s3;
1427 s3.truncate(12);
1428 s2.truncate(0);
1429 }
1430
1431 // while (fNum == 4) {SimpleThread::sleep(10000);} // Force a failure by preventing thread from finishing
1432 fTraceInfo = 2;
1433 }
1434
1435 };
1436
1437 // ** The actual test function.
1438
1439 void MultithreadTest::TestString()
1440 {
1441 int patience;
1442 int terrs = 0;
1443 int j;
1444
1445 UnicodeString *testString = new UnicodeString("This is the original test string.");
1446
1447 StringThreadTest2 *tests[kStringThreadThreads];
1448 for(j = 0; j < kStringThreadThreads; j++) {
1449 tests[j] = new StringThreadTest2(testString, j);
1450 }
1451
1452 logln(UnicodeString("Spawning: ") + kStringThreadThreads + " threads * " + kStringThreadIterations + " iterations each.");
1453 for(j = 0; j < kStringThreadThreads; j++) {
1454 int32_t threadStatus = tests[j]->start();
1455 if (threadStatus != 0) {
1456 errln("System Error %d starting thread number %d.", threadStatus, j);
1457 SimpleThread::errorFunc();
1458 goto cleanupAndReturn;
1459 }
1460 }
1461
1462 for(patience = kStringThreadPatience;patience > 0; patience --)
1463 {
1464 logln("Waiting...");
1465
1466 int32_t i;
1467 terrs = 0;
1468 int32_t completed =0;
1469
1470 for(i=0;i<kStringThreadThreads;i++) {
1471 if (tests[i]->isRunning() == FALSE)
1472 {
1473 completed++;
1474
1475 logln(UnicodeString("Test #") + i + " is complete.. ");
1476
1477 UnicodeString theErr;
1478 if(tests[i]->getError(theErr))
1479 {
1480 terrs++;
1481 errln(UnicodeString("#") + i + ": " + theErr);
1482 }
1483 // print out the error, too, if any.
1484 }
1485 }
1486
1487 if(completed == kStringThreadThreads)
1488 {
1489 logln("Done!");
1490 if(terrs) {
1491 errln("There were errors.");
1492 }
1493 break;
1494 }
1495
1496 SimpleThread::sleep(900);
1497 }
1498
1499 if (patience <= 0) {
1500 errln("patience exceeded. ");
1501 // while (TRUE) {SimpleThread::sleep(10000);} // TODO: for debugging. Sleep forever on failure.
1502 terrs++;
1503 }
1504
1505 if (terrs > 0) {
1506 SimpleThread::errorFunc();
1507 }
1508
1509 cleanupAndReturn:
1510 if (terrs == 0) {
1511 /*
1512 Don't clean up if there are errors. This prevents crashes if the
1513 threads are still running and using this data. This will only happen
1514 if there is an error with the test, ICU, or the machine is too slow.
1515 It's better to leak than crash.
1516 */
1517 for(j = 0; j < kStringThreadThreads; j++) {
1518 delete tests[j];
1519 }
1520 delete testString;
1521 }
1522 }
1523
1524
1525
1526
1527
1528 #endif // ICU_USE_THREADS
1529