/******************************************************************** * COPYRIGHT: * Copyright (c) 2001, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ #if defined(hpux) # ifndef _INCLUDE_POSIX_SOURCE # define _INCLUDE_POSIX_SOURCE # endif #endif #include // Just turn off threads on cygwin, so that we can test // the other stuff. This needs to be investigated further. #if defined(U_CYGWIN) #define ICU_USE_THREADS 0 #endif #if !defined(WIN32) && !defined(XP_MAC) && !defined(U_RHAPSODY) #define POSIX 1 #endif #if defined(POSIX) || defined(U_SOLARIS) || defined(AIX) || defined(HPUX) #define HAVE_IMP #if (ICU_USE_THREADS == 1) #include #endif #if defined(__hpux) && defined(HPUX_CMA) # if defined(read) // read being defined as cma_read causes trouble with iostream::read # undef read # endif #endif /* Define __EXTENSIONS__ for Solaris and old friends in strict mode. */ #ifndef __EXTENSIONS__ #define __EXTENSIONS__ #endif #include /* Define _XPG4_2 for Solaris and friends. */ #ifndef _XPG4_2 #define _XPG4_2 #endif /* Define __USE_XOPEN_EXTENDED for Linux and glibc. */ #ifndef __USE_XOPEN_EXTENDED #define __USE_XOPEN_EXTENDED #endif /* Define _INCLUDE_XOPEN_SOURCE_EXTENDED for HP/UX (11?). */ #ifndef _INCLUDE_XOPEN_SOURCE_EXTENDED #define _INCLUDE_XOPEN_SOURCE_EXTENDED #endif #include #endif /* HPUX */ #ifdef sleep #undef sleep #endif #include "unicode/utypes.h" /* APP_NO_THREADS is an old symbol. We'll honour it if present. */ #ifdef APP_NO_THREADS # define ICU_USE_THREADS 0 #endif /* Default: use threads. */ #ifndef ICU_USE_THREADS # define ICU_USE_THREADS 1 #endif #include "tsmthred.h" MultithreadTest::MultithreadTest() { } MultithreadTest::~MultithreadTest() { } #if (ICU_USE_THREADS==0) void MultithreadTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* par ) { if (exec) logln("TestSuite MultithreadTest: "); if(index == 0) name = "NO_THREADED_TESTS"; else name = ""; if(exec) { logln("MultithreadTest - test DISABLED. ICU_USE_THREADS set to 0, check your configuration if this is a problem.."); } } #else // Note: A LOT OF THE FUNCTIONS IN THIS FILE SHOULD LIVE ELSEWHERE!!!!! // Note: A LOT OF THE FUNCTIONS IN THIS FILE SHOULD LIVE ELSEWHERE!!!!! // -srl #include #include #include // tolower, toupper #include "unicode/putil.h" /* for mthreadtest*/ #include "unicode/numfmt.h" #include "unicode/choicfmt.h" #include "unicode/msgfmt.h" #include "unicode/locid.h" #ifdef WIN32 #define HAVE_IMP # define VC_EXTRALEAN # define WIN32_LEAN_AND_MEAN # define NOGDI # define NOUSER # define NOSERVICE # define NOIME # define NOMCX #include struct Win32ThreadImplementation { HANDLE fHandle; DWORD fThreadID; }; extern "C" unsigned long _stdcall SimpleThreadProc(void *arg) { ((SimpleThread*)arg)->run(); return 0; } SimpleThread::SimpleThread() :fImplementation(0) { Win32ThreadImplementation *imp = new Win32ThreadImplementation; imp->fHandle = 0; imp->fThreadID = 0; fImplementation = imp; } SimpleThread::~SimpleThread() { delete (Win32ThreadImplementation*)fImplementation; } void SimpleThread::start() { Win32ThreadImplementation *imp = (Win32ThreadImplementation*)fImplementation; if(imp->fHandle != NULL) return; imp->fHandle = CreateThread(NULL,0,SimpleThreadProc,(void*)this,0,&imp->fThreadID); } void SimpleThread::sleep(int32_t millis) { ::Sleep(millis); } #elif defined XP_MAC // since the Mac has no preemptive threading (at least on MacOS 8), only // cooperative threading, threads are a no-op. We have no yield() calls // anywhere in the ICU, so we are guaranteed to be thread-safe. #define HAVE_IMP SimpleThread::SimpleThread() {} SimpleThread::~SimpleThread() {} void SimpleThread::start() {} void SimpleThread::run() {} void SimpleThread::sleep(int32_t millis) {} #endif #if defined(POSIX)||defined(U_SOLARIS)||defined(AIX)||defined(HPUX) #define HAVE_IMP struct PosixThreadImplementation { pthread_t fThread; }; extern "C" void* SimpleThreadProc(void *arg) { ((SimpleThread*)arg)->run(); return 0; } SimpleThread::SimpleThread() :fImplementation(0) { PosixThreadImplementation *imp = new PosixThreadImplementation; fImplementation = imp; } SimpleThread::~SimpleThread() { delete (PosixThreadImplementation*)fImplementation; } void SimpleThread::start() { PosixThreadImplementation *imp = (PosixThreadImplementation*)fImplementation; int32_t rc; pthread_attr_t attr; #ifdef HPUX_CMA rc = pthread_attr_create(&attr); rc = pthread_create(&(imp->fThread),attr,&SimpleThreadProc,(void*)this); pthread_attr_delete(&attr); #else rc = pthread_attr_init(&attr); rc = pthread_create(&(imp->fThread),&attr,&SimpleThreadProc,(void*)this); pthread_attr_destroy(&attr); #endif } void SimpleThread::sleep(int32_t millis) { #ifdef U_SOLARIS sigignore(SIGALRM); #endif #ifdef HPUX_CMA cma_sleep(millis/100); #elif defined(HPUX) || defined(OS390) millis *= 1000; while(millis >= 1000000) { usleep(999999); millis -= 1000000; } if(millis > 0) { usleep(millis); } #else usleep(millis * 1000); #endif } #endif // end POSIX #ifndef HAVE_IMP #error No implementation for threads! Cannot test. 0 = 216; //die #endif // *************** end fluff ****************** /* now begins the real test. */ void MultithreadTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) { if (exec) logln("TestSuite MultithreadTest: "); switch (index) { case 0: name = "TestThreads"; if (exec) TestThreads(); break; case 1: name = "TestMutex"; if (exec) TestMutex(); break; case 2: name = "TestThreadedIntl"; #if !UCONFIG_NO_FORMATTING if (exec) TestThreadedIntl(); #endif break; default: name = ""; break; //needed to end loop } } /* TestThreads -- see if threads really work at all. Set up N threads pointing at N chars. When they are started, they will each sleep 1 second and then set their chars. At the end we make sure they are all set. */ #define THREADTEST_NRTHREADS 8 class TestThreadsThread : public SimpleThread { public: TestThreadsThread(char* whatToChange) { fWhatToChange = whatToChange; } virtual void run() { SimpleThread::sleep(1000); *fWhatToChange = '*'; } private: char *fWhatToChange; }; void MultithreadTest::TestThreads() { char threadTestChars[THREADTEST_NRTHREADS + 1]; SimpleThread *threads[THREADTEST_NRTHREADS]; int32_t i; for(i=0;i" + UnicodeString(threadTestChars) + "<- Firing off threads.. "); for(i=0;istart(); SimpleThread::sleep(200); logln(" Subthread started."); } logln("Waiting for threads to be set.."); int32_t patience = 40; // seconds to wait while(patience--) { int32_t count = 0; for(i=0;i" + UnicodeString(threadTestChars) + "<- Got all threads! cya"); for(i=0;i" + UnicodeString(threadTestChars) + "<- Waiting.."); SimpleThread::sleep(500); } errln("->" + UnicodeString(threadTestChars) + "<- PATIENCE EXCEEDED!! Still missing some."); for(i=0;i 0;patience--) { if(thread1.fDone && verbose) printf("Thread1 done\n"); if(thread1.fDone && thread2.fDone) { if(thread2.fErr) errln("Thread 2 says: thread1 didn't run before I aquired the mutex."); logln("took %lu seconds for thread2 to aquire the mutex.", thread2.fElapsed); return; } SimpleThread::sleep(1000); } if(verbose) printf("patience exceeded. [WARNING mutex may still be acquired.] "); } // *********** // *********** TestMultithreadedIntl. Test the ICU in a multithreaded way. // ** First, some utility classes. // ///* Here is an idea which needs more work // TestATest simply runs another Intltest subset against itself. // The correct subset of intltest that should be run in this way should be identified. // */ // //class TestATest : public SimpleThread //{ //public: // TestATest(IntlTest &t) : fTest(t), fDone(FALSE) {} // virtual void run() // { // fTest.runTest(NULL,"TestNumberSpelloutFormat"); // fErrs = fTest.getErrors(); // fDone = TRUE; // } //public: // IntlTest &fTest; // UBool fDone; // int32_t fErrs; //}; // // //#include "itutil.h" ////#include "tscoll.h" ////#include "ittxtbd.h" //#include "itformat.h" ////#include "itcwrap.h" // ///* main code was: // IntlTestFormat formatTest; //// IntlTestCollator collatorTest; // // #define NUMTESTS 2 // TestATest tests[NUMTESTS] = { TestATest(formatTest), TestATest(formatTest) }; // char testName[NUMTESTS][20] = { "formatTest", "formatTest2" }; //*/ #include // * Show exactly where the string's differences lie. UnicodeString showDifference(const UnicodeString& expected, const UnicodeString& result) { UnicodeString res; res = expected + " 0); } UBool getError(UnicodeString& fillinError) { fillinError = fErrorString; return (fErrors > 0); } virtual ~ThreadWithStatus(){} protected: ThreadWithStatus() : fDone(FALSE), fErrors(0) {} void done() { fDone = TRUE; } void error(const UnicodeString &error) { fErrors++; fErrorString = error; done(); } void error() { error("An error occured."); } private: UBool fDone; int32_t fErrors; UnicodeString fErrorString; }; #if !UCONFIG_NO_FORMATTING // ** FormatThreadTest - a thread that tests performing a number of numberformats. #define kFormatThreadIterations 20 // # of iterations per thread #define kFormatThreadThreads 10 // # of threads to spawn #define kFormatThreadPatience 60 // time in seconds to wait for all threads struct FormatThreadTestData { double number; UnicodeString string; FormatThreadTestData(double a, const UnicodeString& b) : number(a),string(b) {} } ; void errorToString(UErrorCode theStatus, UnicodeString &string) { string=u_errorName(theStatus); } // "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3 number,currency}." void formatErrorMessage(UErrorCode &realStatus, const UnicodeString& pattern, const Locale& theLocale, UErrorCode inStatus0, /* statusString 1 */ const Locale &inCountry2, double currency3, // these numbers are the message arguments. UnicodeString &result) { if(U_FAILURE(realStatus)) return; // you messed up UnicodeString errString1; errorToString(inStatus0, errString1); UnicodeString countryName2; inCountry2.getDisplayCountry(theLocale,countryName2); Formattable myArgs[] = { Formattable((int32_t)inStatus0), // inStatus0 {0} Formattable(errString1), // statusString1 {1} Formattable(countryName2), // inCountry2 {2} Formattable(currency3)// currency3 {3,number,currency} }; MessageFormat *fmt = new MessageFormat("MessageFormat's API is broken!!!!!!!!!!!",realStatus); fmt->setLocale(theLocale); fmt->applyPattern(pattern, realStatus); if (U_FAILURE(realStatus)) { delete fmt; return; } FieldPosition ignore = 0; fmt->format(myArgs,4,result,ignore,realStatus); delete fmt; }; class FormatThreadTest : public ThreadWithStatus { public: FormatThreadTest() // constructor is NOT multithread safe. : ThreadWithStatus(), fOffset(0) // the locale to use { static int32_t fgOffset = 0; fgOffset += 3; fOffset = fgOffset; } virtual void run() { // Keep this data here to avoid static initialization. FormatThreadTestData kNumberFormatTestData[] = { FormatThreadTestData((double)5.0, UnicodeString("5", "")), FormatThreadTestData( 6.0, UnicodeString("6", "")), FormatThreadTestData( 20.0, UnicodeString("20", "")), FormatThreadTestData( 8.0, UnicodeString("8", "")), FormatThreadTestData( 8.3, UnicodeString("8.3", "")), FormatThreadTestData( 12345, UnicodeString("12,345", "")), FormatThreadTestData( 81890.23, UnicodeString("81,890.23", "")), }; int32_t kNumberFormatTestDataLength = (int32_t)(sizeof(kNumberFormatTestData) / sizeof(kNumberFormatTestData[0])); // Keep this data here to avoid static initialization. FormatThreadTestData kPercentFormatTestData[] = { FormatThreadTestData((double)5.0, UnicodeString("500%", "")), FormatThreadTestData( 1.0, UnicodeString("100%", "")), FormatThreadTestData( 0.26, UnicodeString("26%", "")), FormatThreadTestData( 16384.99, CharsToUnicodeString("1\\u00a0638\\u00a0499%") ), // U+00a0 = NBSP FormatThreadTestData( 81890.23, CharsToUnicodeString("8\\u00a0189\\u00a0023%" )), }; int32_t kPercentFormatTestDataLength = (int32_t)(sizeof(kPercentFormatTestData) / sizeof(kPercentFormatTestData[0])); int32_t iteration; UErrorCode status = U_ZERO_ERROR; NumberFormat *formatter = NumberFormat::createInstance(Locale::getEnglish(),status); if(U_FAILURE(status)) { error("Error on NumberFormat::createInstance()"); return; } NumberFormat *percentFormatter = NumberFormat::createPercentInstance(Locale::getFrench(),status); if(U_FAILURE(status)) { error("Error on NumberFormat::createPercentInstance()"); delete formatter; return; } for(iteration = 0;!getError() && iterationformat(kNumberFormatTestData[whichLine].number, output); if(0 != output.compare(kNumberFormatTestData[whichLine].string)) { error("format().. expected " + kNumberFormatTestData[whichLine].string + " got " + output); continue; // will break } // Now check percent. output.remove(); whichLine = (iteration + fOffset)%kPercentFormatTestDataLength; percentFormatter->format(kPercentFormatTestData[whichLine].number, output); if(0 != output.compare(kPercentFormatTestData[whichLine].string)) { error("percent format().. \n" + showDifference(kPercentFormatTestData[whichLine].string,output)); continue; } // Test message error #define kNumberOfMessageTests 3 UErrorCode statusToCheck; UnicodeString patternToCheck; Locale messageLocale; Locale countryToCheck; double currencyToCheck; UnicodeString expected; // load the cases. switch((iteration+fOffset) % kNumberOfMessageTests) { default: case 0: statusToCheck= U_FILE_ACCESS_ERROR; patternToCheck= "0:Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3,number,currency}."; // number,currency messageLocale= Locale("en","US"); countryToCheck= Locale("","HR"); currencyToCheck= 8192.77; expected= "0:Someone from Croatia is receiving a #4 error - U_FILE_ACCESS_ERROR. Their telephone call is costing $8,192.77."; break; case 1: statusToCheck= U_INDEX_OUTOFBOUNDS_ERROR; patternToCheck= "1:A customer in {2} is receiving a #{0} error - {1}. Their telephone call is costing {3,number,currency}."; // number,currency messageLocale= Locale("de","DE_PREEURO"); countryToCheck= Locale("","BF"); currencyToCheck= 2.32; expected= "1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. Their telephone call is costing $2.32."; case 2: statusToCheck= U_MEMORY_ALLOCATION_ERROR; patternToCheck= "2:user in {2} is receiving a #{0} error - {1}. They insist they just spent {3,number,currency} on memory."; // number,currency messageLocale= Locale("de","AT_PREEURO"); // Austrian German countryToCheck= Locale("","US"); // hmm currencyToCheck= 40193.12; expected= CharsToUnicodeString("2:user in Vereinigte Staaten is receiving a #7 error - U_MEMORY_ALLOCATION_ERROR. They insist they just spent \\u00f6S 40.193,12 on memory."); break; } UnicodeString result; UErrorCode status = U_ZERO_ERROR; formatErrorMessage(status,patternToCheck,messageLocale,statusToCheck,countryToCheck,currencyToCheck,result); if(U_FAILURE(status)) { UnicodeString tmp; errorToString(status,tmp); error("Failure on message format, pattern=" + patternToCheck +", error = " + tmp); continue; } if(result != expected) { error("PatternFormat: \n" + showDifference(expected,result)); continue; } } delete formatter; delete percentFormatter; done(); } private: int32_t fOffset; // where we are testing from. }; // ** The actual test function. void MultithreadTest::TestThreadedIntl() { FormatThreadTest tests[kFormatThreadThreads]; logln(UnicodeString("Spawning: ") + kFormatThreadThreads + " threads * " + kFormatThreadIterations + " iterations each."); for(int32_t j = 0; j < kFormatThreadThreads; j++) tests[j].start(); for(int32_t patience = kFormatThreadPatience;patience > 0; patience --) { logln("Waiting..."); int32_t i; int32_t terrs = 0; int32_t completed =0; for(i=0;i