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