diff options
Diffstat (limited to 'Build/source/libs/icu/icu-50.1/test/threadtest/threadtest.cpp')
-rw-r--r-- | Build/source/libs/icu/icu-50.1/test/threadtest/threadtest.cpp | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/Build/source/libs/icu/icu-50.1/test/threadtest/threadtest.cpp b/Build/source/libs/icu/icu-50.1/test/threadtest/threadtest.cpp new file mode 100644 index 00000000000..85e53a552a4 --- /dev/null +++ b/Build/source/libs/icu/icu-50.1/test/threadtest/threadtest.cpp @@ -0,0 +1,537 @@ +// +//******************************************************************** +// Copyright (C) 2002-2011, International Business Machines +// Corporation and others. All Rights Reserved. +//******************************************************************** +// +// File threadtest.cpp +// + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "unicode/utypes.h" +#include "unicode/uclean.h" +#include "umutex.h" +#include "threadtest.h" + +AbstractThreadTest::~AbstractThreadTest() {} + +//------------------------------------------------------------------------------ +// +// Factory functions for creating different test types. +// +//------------------------------------------------------------------------------ +extern AbstractThreadTest *createStringTest(); +extern AbstractThreadTest *createConvertTest(); + + + +//------------------------------------------------------------------------------ +// +// Windows specific code for starting threads +// +//------------------------------------------------------------------------------ +#if U_PLATFORM_USES_ONLY_WIN32_API + +#include "Windows.h" +#include "process.h" + + + +typedef void (*ThreadFunc)(void *); + +class ThreadFuncs // This class isolates OS dependent threading +{ // functions from the rest of ThreadTest program. +public: + static void Sleep(int millis) {::Sleep(millis);}; + static void startThread(ThreadFunc, void *param); + static unsigned long getCurrentMillis(); + static void yield() {::Sleep(0);}; +}; + +void ThreadFuncs::startThread(ThreadFunc func, void *param) +{ + unsigned long x; + x = _beginthread(func, 0x10000, param); + if (x == -1) + { + fprintf(stderr, "Error starting thread. Errno = %d\n", errno); + exit(-1); + } +} + +unsigned long ThreadFuncs::getCurrentMillis() +{ + return (unsigned long)::GetTickCount(); +} + + + + +// #elif defined (POSIX) +#else + +//------------------------------------------------------------------------------ +// +// UNIX specific code for starting threads +// +//------------------------------------------------------------------------------ +#include <pthread.h> +#include <unistd.h> +#include <errno.h> +#include <sched.h> +#include <sys/timeb.h> + + +extern "C" { + + +typedef void (*ThreadFunc)(void *); +typedef void *(*pthreadfunc)(void *); + +class ThreadFuncs // This class isolates OS dependent threading +{ // functions from the rest of ThreadTest program. +public: + static void Sleep(int millis); + static void startThread(ThreadFunc, void *param); + static unsigned long getCurrentMillis(); + static void yield() {sched_yield();}; +}; + +void ThreadFuncs::Sleep(int millis) +{ + int seconds = millis/1000; + if (seconds <= 0) seconds = 1; + ::sleep(seconds); +} + + +void ThreadFuncs::startThread(ThreadFunc func, void *param) +{ + unsigned long x; + + pthread_t tId; + //thread_t tId; +#if defined(_HP_UX) && defined(XML_USE_DCE) + x = pthread_create( &tId, pthread_attr_default, (pthreadfunc)func, param); +#else + pthread_attr_t attr; + pthread_attr_init(&attr); + x = pthread_create( &tId, &attr, (pthreadfunc)func, param); +#endif + if (x == -1) + { + fprintf(stderr, "Error starting thread. Errno = %d\n", errno); + exit(-1); + } +} + +unsigned long ThreadFuncs::getCurrentMillis() { + timeb aTime; + ftime(&aTime); + return (unsigned long)(aTime.time*1000 + aTime.millitm); +} +} + + +// #else +// #error This platform is not supported +#endif + + + +//------------------------------------------------------------------------------ +// +// struct runInfo Holds the info extracted from the command line and data +// that is shared by all threads. +// There is only one of these, and it is static. +// During the test, the threads will access this info without +// any synchronization. +// +//------------------------------------------------------------------------------ +const int MAXINFILES = 25; +struct RunInfo +{ + bool quiet; + bool verbose; + int numThreads; + int totalTime; + int checkTime; + AbstractThreadTest *fTest; + bool stopFlag; + bool exitFlag; + int32_t runningThreads; +}; + + +//------------------------------------------------------------------------------ +// +// struct threadInfo Holds information specific to an individual thread. +// One of these is set up for each thread in the test. +// The main program monitors the threads by looking +// at the status stored in these structs. +// +//------------------------------------------------------------------------------ +struct ThreadInfo +{ + bool fHeartBeat; // Set true by the thread each time it finishes + // a test. + unsigned int fCycles; // Number of cycles completed. + int fThreadNum; // Identifying number for this thread. + ThreadInfo() { + fHeartBeat = false; + fCycles = 0; + fThreadNum = -1; + } +}; + + +// +//------------------------------------------------------------------------------ +// +// Global Data +// +//------------------------------------------------------------------------------ +RunInfo gRunInfo; +ThreadInfo *gThreadInfo; +UMTX gStopMutex; // Lets main thread suspend test threads. +UMTX gInfoMutex; // Synchronize access to data passed between + // worker threads and the main thread + + +//---------------------------------------------------------------------- +// +// parseCommandLine Read through the command line, and save all +// of the options in the gRunInfo struct. +// +// Display the usage message if the command line +// is no good. +// +// Probably ought to be a member function of RunInfo. +// +//---------------------------------------------------------------------- + +void parseCommandLine(int argc, char **argv) +{ + gRunInfo.quiet = false; // Set up defaults for run. + gRunInfo.verbose = false; + gRunInfo.numThreads = 2; + gRunInfo.totalTime = 0; + gRunInfo.checkTime = 10; + + try // Use exceptions for command line syntax errors. + { + int argnum = 1; + while (argnum < argc) + { + if (strcmp(argv[argnum], "-quiet") == 0) + gRunInfo.quiet = true; + else if (strcmp(argv[argnum], "-verbose") == 0) + gRunInfo.verbose = true; + else if (strcmp(argv[argnum], "--help") == 0 || + (strcmp(argv[argnum], "?") == 0)) {throw 1; } + + else if (strcmp(argv[argnum], "-threads") == 0) + { + ++argnum; + if (argnum >= argc) + throw 1; + gRunInfo.numThreads = atoi(argv[argnum]); + if (gRunInfo.numThreads < 0) + throw 1; + } + else if (strcmp(argv[argnum], "-time") == 0) + { + ++argnum; + if (argnum >= argc) + throw 1; + gRunInfo.totalTime = atoi(argv[argnum]); + if (gRunInfo.totalTime < 1) + throw 1; + } + else if (strcmp(argv[argnum], "-ctime") == 0) + { + ++argnum; + if (argnum >= argc) + throw 1; + gRunInfo.checkTime = atoi(argv[argnum]); + if (gRunInfo.checkTime < 1) + throw 1; + } + else if (strcmp(argv[argnum], "string") == 0) + { + gRunInfo.fTest = createStringTest(); + } + else if (strcmp(argv[argnum], "convert") == 0) + { + gRunInfo.fTest = createConvertTest(); + } + else + { + fprintf(stderr, "Unrecognized command line option. Scanning \"%s\"\n", + argv[argnum]); + throw 1; + } + argnum++; + } + // We've reached the end of the command line parameters. + // Fail if no test name was specified. + if (gRunInfo.fTest == NULL) { + fprintf(stderr, "No test specified.\n"); + throw 1; + } + + } + catch (int) + { + fprintf(stderr, "usage: threadtest [-threads nnn] [-time nnn] [-quiet] [-verbose] test-name\n" + " -quiet Suppress periodic status display. \n" + " -verbose Display extra messages. \n" + " -threads nnn Number of threads. Default is 2. \n" + " -time nnn Total time to run, in seconds. Default is forever.\n" + " -ctime nnn Time between extra consistency checks, in seconds. Default 10\n" + " testname string | convert\n" + ); + exit(1); + } +} + + + + + +//---------------------------------------------------------------------- +// +// threadMain The main function for each of the swarm of test threads. +// Run in a loop, executing the runOnce() test function each time. +// +// +//---------------------------------------------------------------------- + +extern "C" { + +void threadMain (void *param) +{ + ThreadInfo *thInfo = (ThreadInfo *)param; + + if (gRunInfo.verbose) + printf("Thread #%d: starting\n", thInfo->fThreadNum); + umtx_atomic_inc(&gRunInfo.runningThreads); + + // + // + while (true) + { + if (gRunInfo.verbose ) + printf("Thread #%d: starting loop\n", thInfo->fThreadNum); + + // + // If the main thread is asking us to wait, do so by locking gStopMutex + // which will block us, since the main thread will be holding it already. + // + umtx_lock(&gInfoMutex); + UBool stop = gRunInfo.stopFlag; // Need mutex for processors with flakey memory models. + umtx_unlock(&gInfoMutex); + + if (stop) { + if (gRunInfo.verbose) { + fprintf(stderr, "Thread #%d: suspending\n", thInfo->fThreadNum); + } + umtx_atomic_dec(&gRunInfo.runningThreads); + while (gRunInfo.stopFlag) { + umtx_lock(&gStopMutex); + umtx_unlock(&gStopMutex); + } + umtx_atomic_inc(&gRunInfo.runningThreads); + if (gRunInfo.verbose) { + fprintf(stderr, "Thread #%d: restarting\n", thInfo->fThreadNum); + } + } + + // + // The real work of the test happens here. + // + gRunInfo.fTest->runOnce(); + + umtx_lock(&gInfoMutex); + thInfo->fHeartBeat = true; + thInfo->fCycles++; + UBool exitNow = gRunInfo.exitFlag; + umtx_unlock(&gInfoMutex); + + // + // If main thread says it's time to exit, break out of the loop. + // + if (exitNow) { + break; + } + } + + umtx_atomic_dec(&gRunInfo.runningThreads); + + // Returning will kill the thread. + return; +} + +} + + + + +//---------------------------------------------------------------------- +// +// main +// +//---------------------------------------------------------------------- + +int main (int argc, char **argv) +{ + // + // Parse the command line options, and create the specified kind of test. + // + parseCommandLine(argc, argv); + + + // + // Fire off the requested number of parallel threads + // + + if (gRunInfo.numThreads == 0) + exit(0); + + gRunInfo.exitFlag = FALSE; + gRunInfo.stopFlag = TRUE; // Will cause the new threads to block + umtx_lock(&gStopMutex); + + gThreadInfo = new ThreadInfo[gRunInfo.numThreads]; + int threadNum; + for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) + { + gThreadInfo[threadNum].fThreadNum = threadNum; + ThreadFuncs::startThread(threadMain, &gThreadInfo[threadNum]); + } + + + unsigned long startTime = ThreadFuncs::getCurrentMillis(); + int elapsedSeconds = 0; + int timeSinceCheck = 0; + + // + // Unblock the threads. + // + gRunInfo.stopFlag = FALSE; // Unblocks the worker threads. + umtx_unlock(&gStopMutex); + + // + // Loop, watching the heartbeat of the worker threads. + // Each second, + // display "+" if all threads have completed at least one loop + // display "." if some thread hasn't since previous "+" + // Each "ctime" seconds, + // Stop all the worker threads at the top of their loop, then + // call the test's check function. + // + while (gRunInfo.totalTime == 0 || gRunInfo.totalTime > elapsedSeconds) + { + ThreadFuncs::Sleep(1000); // We sleep while threads do their work ... + + if (gRunInfo.quiet == false && gRunInfo.verbose == false) + { + char c = '+'; + int threadNum; + umtx_lock(&gInfoMutex); + for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) + { + if (gThreadInfo[threadNum].fHeartBeat == false) + { + c = '.'; + break; + }; + } + umtx_unlock(&gInfoMutex); + fputc(c, stdout); + fflush(stdout); + if (c == '+') + for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) + gThreadInfo[threadNum].fHeartBeat = false; + } + + // + // Update running times. + // + timeSinceCheck -= elapsedSeconds; + elapsedSeconds = (ThreadFuncs::getCurrentMillis() - startTime) / 1000; + timeSinceCheck += elapsedSeconds; + + // + // Call back to the test to let it check its internal validity + // + if (timeSinceCheck >= gRunInfo.checkTime) { + if (gRunInfo.verbose) { + fprintf(stderr, "Main: suspending all threads\n"); + } + umtx_lock(&gStopMutex); // Block the worker threads at the top of their loop + gRunInfo.stopFlag = TRUE; + for (;;) { + umtx_lock(&gInfoMutex); + UBool done = gRunInfo.runningThreads == 0; + umtx_unlock(&gInfoMutex); + if (done) { break;} + ThreadFuncs::yield(); + } + + + + gRunInfo.fTest->check(); + if (gRunInfo.quiet == false && gRunInfo.verbose == false) { + fputc('C', stdout); + } + + if (gRunInfo.verbose) { + fprintf(stderr, "Main: starting all threads.\n"); + } + gRunInfo.stopFlag = FALSE; // Unblock the worker threads. + umtx_unlock(&gStopMutex); + timeSinceCheck = 0; + } + }; + + // + // Time's up, we are done. (We only get here if this was a timed run) + // Tell the threads to exit. + // + gRunInfo.exitFlag = true; + for (;;) { + umtx_lock(&gInfoMutex); + UBool done = gRunInfo.runningThreads == 0; + umtx_unlock(&gInfoMutex); + if (done) { break;} + ThreadFuncs::yield(); + } + + // + // Tally up the total number of cycles completed by each of the threads. + // + double totalCyclesCompleted = 0; + for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) { + totalCyclesCompleted += gThreadInfo[threadNum].fCycles; + } + + double cyclesPerMinute = totalCyclesCompleted / (double(gRunInfo.totalTime) / double(60)); + printf("\n%8.1f cycles per minute.", cyclesPerMinute); + + // + // Memory should be clean coming out + // + delete gRunInfo.fTest; + delete [] gThreadInfo; + umtx_destroy(&gInfoMutex); + umtx_destroy(&gStopMutex); + u_cleanup(); + + return 0; +} + + |