/******************************************************************** * COPYRIGHT: * Copyright (c) 1999-2004, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ #if defined(hpux) # ifndef _INCLUDE_POSIX_SOURCE # define _INCLUDE_POSIX_SOURCE # endif #endif #include "unicode/utypes.h" #include "unicode/ustring.h" #include "umutex.h" #include "cmemory.h" #include "cstring.h" #include "uparse.h" #include "unicode/resbund.h" #include "unicode/udata.h" #include "unicode/uloc.h" #include "unicode/locid.h" #if !defined(WIN32) && !defined(XP_MAC) && !defined(U_RHAPSODY) #define POSIX 1 #endif #if defined(POSIX) || defined(U_SOLARIS) || defined(U_AIX) || defined(U_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 "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" #include "unicode/ucol.h" #include "ucaconf.h" //----------------------------------------------------------------------------------- // // class SimpleThread Of course we need a thread class first.. // This wrapper has a ported implementation. // //----------------------------------------------------------------------------------- class SimpleThread { public: SimpleThread(); virtual ~SimpleThread(); int32_t start(void); // start the thread UBool isRunning(); // return true if a started thread has exited. virtual void run(void) = 0; // Override this to provide the code to run // in the thread. void *fImplementation; public: static void sleep(int32_t millis); // probably shouldn't go here but oh well. static void errorFunc(); // Empty function, provides a single convenient place // to break on errors. }; void SimpleThread::errorFunc() { // *(char *)0 = 3; // Force entry into a debugger via a crash; } #ifdef WIN32 #define HAVE_IMP # define VC_EXTRALEAN # define WIN32_LEAN_AND_MEAN # define NOGDI # define NOUSER # define NOSERVICE # define NOIME # define NOMCX #include #include //----------------------------------------------------------------------------------- // // class SimpleThread Windows Implementation // //----------------------------------------------------------------------------------- struct Win32ThreadImplementation { HANDLE fHandle; unsigned int fThreadID; }; extern "C" unsigned int __stdcall SimpleThreadProc(void *arg) { ((SimpleThread*)arg)->run(); return 0; } SimpleThread::SimpleThread() :fImplementation(0) { Win32ThreadImplementation *imp = new Win32ThreadImplementation; imp->fHandle = 0; fImplementation = imp; } SimpleThread::~SimpleThread() { // Destructor. Because we start the thread running with _beginthreadex(), // we own the Windows HANDLE for the thread and must // close it here. Win32ThreadImplementation *imp = (Win32ThreadImplementation*)fImplementation; if (imp != 0) { if (imp->fHandle != 0) { CloseHandle(imp->fHandle); imp->fHandle = 0; } } delete (Win32ThreadImplementation*)fImplementation; } int32_t SimpleThread::start() { Win32ThreadImplementation *imp = (Win32ThreadImplementation*)fImplementation; if(imp->fHandle != NULL) { // The thread appears to have already been started. // This is probably an error on the part of our caller. return -1; } imp->fHandle = (HANDLE) _beginthreadex( NULL, // Security 0x20000, // Stack Size SimpleThreadProc, // Function to Run (void *)this, // Arg List 0, // initflag. Start running, not suspended &imp->fThreadID // thraddr ); if (imp->fHandle == 0) { // An error occured int err = errno; if (err == 0) { err = -1; } return err; } return 0; } UBool SimpleThread::isRunning() { // // Test whether the thread associated with the SimpleThread object is // still actually running. // // NOTE: on Win64 on Itanium processors, a crashes // occur if the main thread of a process exits concurrently with some // other thread(s) exiting. To avoid the possibility, we wait until the // OS indicates that all threads have terminated, rather than waiting // only until the end of the user's Run function has been reached. // // I don't know whether the crashes represent a Windows bug, or whether // main() programs are supposed to have to wait for their threads. // Win32ThreadImplementation *imp = (Win32ThreadImplementation*)fImplementation; bool success; DWORD threadExitCode; if (imp->fHandle == 0) { // No handle, thread must not be running. return FALSE; } success = GetExitCodeThread(imp->fHandle, &threadExitCode) != 0; if (! success) { // Can't get status, thread must not be running. return FALSE; } return (threadExitCode == STILL_ACTIVE); } void SimpleThread::sleep(int32_t millis) { ::Sleep(millis); } //----------------------------------------------------------------------------------- // // class SimpleThread NULL Implementation // //----------------------------------------------------------------------------------- #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() {} int32_t SimpleThread::start() { return 0; } void SimpleThread::run() {} void SimpleThread::sleep(int32_t millis) {} UBool SimpleThread::isRunning() { return FALSE; } #endif //----------------------------------------------------------------------------------- // // class SimpleThread POSIX implementation // // A note on the POSIX vs the Windows implementations of this class.. // On Windows, the main thread must verify that other threads have finished // before exiting, or crashes occasionally occur. (Seen on Itanium Win64 only) // The function SimpleThread::isRunning() is used for this purpose. // // On POSIX, there is NO reliable non-blocking mechanism to determine // whether a thread has exited. pthread_kill(thread, 0) almost works, // but the system can recycle thread ids immediately, so seeing that a // thread exists with this call could mean that the original thread has // finished and a new one started with the same ID. Useless. // // So we need to do the check with user code, by setting a flag just before // the thread function returns. A technique that is guaranteed to fail // on Windows, because it indicates that the thread is done before all // system level cleanup has happened. // //----------------------------------------------------------------------------------- #if defined(POSIX)||defined(U_SOLARIS)||defined(U_AIX)||defined(U_HPUX) #define HAVE_IMP struct PosixThreadImplementation { pthread_t fThread; UBool fRunning; UBool fRan; /* True if the thread was successfully started */ }; extern "C" void* SimpleThreadProc(void *arg) { // This is the code that is run in the new separate thread. SimpleThread *This = (SimpleThread *)arg; This->run(); // Run the user code. // The user function has returned. Set the flag indicating that this thread // is done. Need a mutex for memory barrier purposes only, so that other thread // will reliably see that the flag has changed. PosixThreadImplementation *imp = (PosixThreadImplementation*)This->fImplementation; umtx_lock(NULL); imp->fRunning = FALSE; umtx_unlock(NULL); return 0; } SimpleThread::SimpleThread() { PosixThreadImplementation *imp = new PosixThreadImplementation; imp->fRunning = FALSE; imp->fRan = FALSE; fImplementation = imp; } SimpleThread::~SimpleThread() { PosixThreadImplementation *imp = (PosixThreadImplementation*)fImplementation; if (imp->fRan) { pthread_join(imp->fThread, NULL); } delete imp; fImplementation = (void *)0xdeadbeef; } int32_t SimpleThread::start() { int32_t rc; static pthread_attr_t attr; static UBool attrIsInitialized = FALSE; PosixThreadImplementation *imp = (PosixThreadImplementation*)fImplementation; imp->fRunning = TRUE; imp->fRan = TRUE; #ifdef HPUX_CMA if (attrIsInitialized == FALSE) { rc = pthread_attr_create(&attr); attrIsInitialized = TRUE; } rc = pthread_create(&(imp->fThread),attr,&SimpleThreadProc,(void*)this); #else if (attrIsInitialized == FALSE) { rc = pthread_attr_init(&attr); #if defined(OS390) { int detachstate = 0; /* jdc30: detach state of zero causes threads created with this attr to be in an undetached state. An undetached thread will keep its resources after termination. */ pthread_attr_setdetachstate(&attr, &detachstate); } #else // pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); #endif attrIsInitialized = TRUE; } rc = pthread_create(&(imp->fThread),&attr,&SimpleThreadProc,(void*)this); #endif if (rc != 0) { // some kind of error occured, the thread did not start. imp->fRan = FALSE; imp->fRunning = FALSE; } return rc; } UBool SimpleThread::isRunning() { // Note: Mutex functions are used here not for synchronization, // but to force memory barriors to exist, to ensure that one thread // can see changes made by another when running on processors // with memory models having weak coherency. PosixThreadImplementation *imp = (PosixThreadImplementation*)fImplementation; umtx_lock(NULL); UBool retVal = imp->fRunning; umtx_unlock(NULL); return retVal; } void SimpleThread::sleep(int32_t millis) { #ifdef U_SOLARIS sigignore(SIGALRM); #endif #ifdef HPUX_CMA cma_sleep(millis/100); #elif defined(U_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; case 3: name = "TestCollators"; #if !UCONFIG_NO_COLLATION if (exec) { TestCollators(); } #endif /* #if !UCONFIG_NO_COLLATION */ break; case 4: name = "TestString"; if (exec) { TestString(); } 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); Mutex m; *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() != 0) { errln("Error starting thread %d", i); } 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; umtx_lock(NULL); 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--) { // TODO: Possible memory coherence issue in looking at fDone values // that are set in another thread without the mutex here. 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.] "); } //------------------------------------------------------------------------------------------- // // class ThreadWithStatus - a thread that we can check the status and error condition of // //------------------------------------------------------------------------------------------- class ThreadWithStatus : public SimpleThread { public: UBool getError() { return (fErrors > 0); } UBool getError(UnicodeString& fillinError) { fillinError = fErrorString; return (fErrors > 0); } virtual ~ThreadWithStatus(){} protected: ThreadWithStatus() : fErrors(0) {} void error(const UnicodeString &error) { fErrors++; fErrorString = error; SimpleThread::errorFunc(); } void error() { error("An error occured."); } private: int32_t fErrors; UnicodeString fErrorString; }; //------------------------------------------------------------------------------------------- // // TestMultithreadedIntl. Test ICU Formatting n a multi-threaded environment // //------------------------------------------------------------------------------------------- // * Show exactly where the string's differences lie. UnicodeString showDifference(const UnicodeString& expected, const UnicodeString& result) { UnicodeString res; res = expected + "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; }; UBool U_CALLCONV isAcceptable(void *, const char *, const char *, const UDataInfo *) { return TRUE; } //static UMTX debugMutex = NULL; static UMTX gDebugMutex; class FormatThreadTest : public ThreadWithStatus { public: int fNum; int fTraceInfo; FormatThreadTest() // constructor is NOT multithread safe. : ThreadWithStatus(), fNum(0), fTraceInfo(0), fOffset(0) // the locale to use { static int32_t fgOffset = 0; fgOffset += 3; fOffset = fgOffset; } virtual void run() { fTraceInfo = 1; NumberFormat *formatter = NULL; NumberFormat *percentFormatter = NULL; UErrorCode status = U_ZERO_ERROR; #if 0 // debugging code, for (int i=0; i<4000; i++) { status = U_ZERO_ERROR; UDataMemory *data1 = udata_openChoice(0, "res", "en_US", isAcceptable, 0, &status); UDataMemory *data2 = udata_openChoice(0, "res", "fr", isAcceptable, 0, &status); udata_close(data1); udata_close(data2); if (U_FAILURE(status)) { error("udata_openChoice failed.\n"); break; } } return; #endif #if 1 // debugging code, int m; for (m=0; m<4000; m++) { status = U_ZERO_ERROR; UResourceBundle *res = NULL; const char *localeName = NULL; Locale loc = Locale::getEnglish(); localeName = loc.getName(); // localeName = "en"; // ResourceBundle bund = ResourceBundle(0, loc, status); //umtx_lock(&gDebugMutex); res = ures_open(NULL, localeName, &status); //umtx_unlock(&gDebugMutex); //umtx_lock(&gDebugMutex); ures_close(res); //umtx_unlock(&gDebugMutex); if (U_FAILURE(status)) { error("Resource bundle construction failed.\n"); break; } } return; #endif // 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; status = U_ZERO_ERROR; formatter = NumberFormat::createInstance(Locale::getEnglish(),status); if(U_FAILURE(status)) { error("Error on NumberFormat::createInstance()"); goto cleanupAndReturn; } percentFormatter = NumberFormat::createPercentInstance(Locale::getFrench(),status); if(U_FAILURE(status)) { error("Error on NumberFormat::createPercentInstance()"); goto cleanupAndReturn; } for(iteration = 0;!getError() && iterationformat(kNumberFormatTestData[whichLine].number, output); if(0 != output.compare(kNumberFormatTestData[whichLine].string)) { error("format().. expected " + kNumberFormatTestData[whichLine].string + " got " + output); goto cleanupAndReturn; } // 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)); goto cleanupAndReturn; } // Test message error const int 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 DM."; break; 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); goto cleanupAndReturn; } if(result != expected) { error("PatternFormat: \n" + showDifference(expected,result)); goto cleanupAndReturn; } } /* end of for loop */ cleanupAndReturn: delete formatter; delete percentFormatter; // while (fNum == 4) {SimpleThread::sleep(10000);} // Force a failure by preventing thread from finishing fTraceInfo = 2; } private: int32_t fOffset; // where we are testing from. }; // ** The actual test function. void MultithreadTest::TestThreadedIntl() { int i; UnicodeString theErr; UBool haveDisplayedInfo[kFormatThreadThreads]; // // Create and start the test threads // logln("Spawning: %d threads * %d iterations each.", kFormatThreadThreads, kFormatThreadIterations); FormatThreadTest *tests = new FormatThreadTest[kFormatThreadThreads]; for(int32_t j = 0; j < kFormatThreadThreads; j++) { tests[j].fNum = j; int32_t threadStatus = tests[j].start(); if (threadStatus != 0) { errln("System Error %d starting thread number %d.", threadStatus, j); SimpleThread::errorFunc(); goto cleanupAndReturn; } haveDisplayedInfo[j] = FALSE; } // Spin, waiting for the test threads to finish. // (An earlier version used a wait in this loop, but that seems to trigger // a bug in some versions of AIX.) UBool stillRunning; do { /* Spin until the test threads complete. */ stillRunning = FALSE; for(i=0;i 0) { error(UnicodeString("Line %i is not greater or equal than previous line ")+ UnicodeString(i)); break; } else if(res == 0) { /* equal */ res = u_strcmpCodePointOrder(lines[i-1].buff, lines[i].buff); if (res == 0) { error(UnicodeString("Probable error in test file on line %i (comparing identical strings)")+ UnicodeString(i)); break; } else if (res > 0) { error(UnicodeString("Sortkeys are identical, but code point comapare gives >0 on line ")+ UnicodeString(i)); break; } } } oldSk = newSk; oldLen = resLen; newSk = (newSk == sk1)?sk2:sk1; } } }; void MultithreadTest::TestCollators() { UErrorCode status = U_ZERO_ERROR; FILE *testFile = NULL; char testDataPath[1024]; strcpy(testDataPath, IntlTest::loadTestData(status)); char* index = 0; if (U_FAILURE(status)) { errln("ERROR: could not open test data %s", u_errorName(status)); return; } index=strrchr(testDataPath,(char)U_FILE_SEP_CHAR); if((unsigned int)(index-testDataPath) != (strlen(testDataPath)-1)){ *(index+1)=0; } strcat(testDataPath,".."U_FILE_SEP_STRING); strcat(testDataPath, "CollationTest_"); const char* type = "NON_IGNORABLE"; const char *ext = ".txt"; if(testFile) { fclose(testFile); } char buffer[1024]; strcpy(buffer, testDataPath); strcat(buffer, type); size_t bufLen = strlen(buffer); // we try to open 3 files: // path/CollationTest_type.txt // path/CollationTest_type_SHORT.txt // path/CollationTest_type_STUB.txt // we are going to test with the first one that we manage to open. strcpy(buffer+bufLen, ext); testFile = fopen(buffer, "rb"); if(testFile == 0) { strcpy(buffer+bufLen, "_SHORT"); strcat(buffer, ext); testFile = fopen(buffer, "rb"); if(testFile == 0) { strcpy(buffer+bufLen, "_STUB"); strcat(buffer, ext); testFile = fopen(buffer, "rb"); if (testFile == 0) { *(buffer+bufLen) = 0; errln("ERROR: could not open any of the conformance test files, tried opening base %s", buffer); return; } else { infoln( "INFO: Working with the stub file.\n" "If you need the full conformance test, please\n" "download the appropriate data files from:\n" "http://oss.software.ibm.com/cvs/icu4j/unicodetools/com/ibm/text/data/"); } } } Line *lines = new Line[200000]; memset(lines, 0, sizeof(Line)*200000); int32_t lineNum = 0; UChar bufferU[1024]; int32_t buflen = 0; uint32_t first = 0; uint32_t offset = 0; while (fgets(buffer, 1024, testFile) != NULL) { offset = 0; if(*buffer == 0 || buffer[0] == '#') { continue; } offset = u_parseString(buffer, bufferU, 1024, &first, &status); buflen = offset; bufferU[offset++] = 0; lines[lineNum].buflen = buflen; //lines[lineNum].buff = new UChar[buflen+1]; u_memcpy(lines[lineNum].buff, bufferU, buflen); lineNum++; } fclose(testFile); UCollator *coll = ucol_open("root", &status); if(U_FAILURE(status)) { errln("Couldn't open UCA collator"); return; } ucol_setAttribute(coll, UCOL_NORMALIZATION_MODE, UCOL_ON, &status); ucol_setAttribute(coll, UCOL_CASE_FIRST, UCOL_OFF, &status); ucol_setAttribute(coll, UCOL_CASE_LEVEL, UCOL_OFF, &status); ucol_setAttribute(coll, UCOL_STRENGTH, UCOL_TERTIARY, &status); ucol_setAttribute(coll, UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE, &status); int32_t noSpawned = 0; int32_t spawnResult = 0; CollatorThreadTest *tests; tests = new CollatorThreadTest[kCollatorThreadThreads]; logln(UnicodeString("Spawning: ") + kCollatorThreadThreads + " threads * " + kFormatThreadIterations + " iterations each."); int32_t j = 0; for(j = 0; j < kCollatorThreadThreads; j++) { //logln("Setting collator %i", j); tests[j].setCollator(coll, lines, lineNum); } for(j = 0; j < kCollatorThreadThreads; j++) { log("%i ", j); spawnResult = tests[j].start(); if(spawnResult != 0) { infoln("THREAD INFO: Couldn't spawn more than %i threads", noSpawned); break; } noSpawned++; } logln("Spawned all"); //for(int32_t patience = kCollatorThreadPatience;patience > 0; patience --) for(;;) { logln("Waiting..."); int32_t i; int32_t terrs = 0; int32_t completed =0; for(i=0;istart(); if (threadStatus != 0) { errln("System Error %d starting thread number %d.", threadStatus, j); SimpleThread::errorFunc(); goto cleanupAndReturn; } } for(patience = kStringThreadPatience;patience > 0; patience --) { logln("Waiting..."); int32_t i; terrs = 0; int32_t completed =0; for(i=0;iisRunning() == FALSE) { completed++; logln(UnicodeString("Test #") + i + " is complete.. "); UnicodeString theErr; if(tests[i]->getError(theErr)) { terrs++; errln(UnicodeString("#") + i + ": " + theErr); } // print out the error, too, if any. } } if(completed == kStringThreadThreads) { logln("Done!"); if(terrs) { errln("There were errors."); } break; } SimpleThread::sleep(900); } if (patience <= 0) { errln("patience exceeded. "); // while (TRUE) {SimpleThread::sleep(10000);} // TODO: for debugging. Sleep forever on failure. terrs++; } if (terrs > 0) { SimpleThread::errorFunc(); } cleanupAndReturn: for(j = 0; j < kStringThreadThreads; j++) { delete tests[j]; } return; } #endif // ICU_USE_THREADS