Home | History | Annotate | Download | only in threadtest
      1 //
      2 //********************************************************************
      3 //   Copyright (C) 2002-2011, International Business Machines
      4 //   Corporation and others.  All Rights Reserved.
      5 //********************************************************************
      6 //
      7 // File threadtest.cpp
      8 //
      9 
     10 #include <stdlib.h>
     11 #include <stdio.h>
     12 #include <string.h>
     13 
     14 #include "unicode/utypes.h"
     15 #include "unicode/uclean.h"
     16 #include "umutex.h"
     17 #include "threadtest.h"
     18 
     19 AbstractThreadTest::~AbstractThreadTest() {}
     20 
     21 //------------------------------------------------------------------------------
     22 //
     23 //   Factory functions for creating different test types.
     24 //
     25 //------------------------------------------------------------------------------
     26 extern  AbstractThreadTest *createStringTest();
     27 extern  AbstractThreadTest *createConvertTest();
     28 
     29 
     30 
     31 //------------------------------------------------------------------------------
     32 //
     33 //   Windows specific code for starting threads
     34 //
     35 //------------------------------------------------------------------------------
     36 #if U_PLATFORM_USES_ONLY_WIN32_API
     37 
     38 #include "Windows.h"
     39 #include "process.h"
     40 
     41 
     42 
     43 typedef void (*ThreadFunc)(void *);
     44 
     45 class ThreadFuncs           // This class isolates OS dependent threading
     46 {                           //   functions from the rest of ThreadTest program.
     47 public:
     48     static void            Sleep(int millis) {::Sleep(millis);};
     49     static void            startThread(ThreadFunc, void *param);
     50     static unsigned long   getCurrentMillis();
     51     static void            yield() {::Sleep(0);};
     52 };
     53 
     54 void ThreadFuncs::startThread(ThreadFunc func, void *param)
     55 {
     56     unsigned long x;
     57     x = _beginthread(func, 0x10000, param);
     58     if (x == -1)
     59     {
     60         fprintf(stderr, "Error starting thread.  Errno = %d\n", errno);
     61         exit(-1);
     62     }
     63 }
     64 
     65 unsigned long ThreadFuncs::getCurrentMillis()
     66 {
     67     return (unsigned long)::GetTickCount();
     68 }
     69 
     70 
     71 
     72 
     73 // #elif defined (POSIX)
     74 #else
     75 
     76 //------------------------------------------------------------------------------
     77 //
     78 //   UNIX specific code for starting threads
     79 //
     80 //------------------------------------------------------------------------------
     81 #include <pthread.h>
     82 #include <unistd.h>
     83 #include <errno.h>
     84 #include <sched.h>
     85 #include <sys/timeb.h>
     86 
     87 
     88 extern "C" {
     89 
     90 
     91 typedef void (*ThreadFunc)(void *);
     92 typedef void *(*pthreadfunc)(void *);
     93 
     94 class ThreadFuncs           // This class isolates OS dependent threading
     95 {                           //   functions from the rest of ThreadTest program.
     96 public:
     97     static void            Sleep(int millis);
     98     static void            startThread(ThreadFunc, void *param);
     99     static unsigned long   getCurrentMillis();
    100     static void            yield() {sched_yield();};
    101 };
    102 
    103 void ThreadFuncs::Sleep(int millis)
    104 {
    105    int seconds = millis/1000;
    106    if (seconds <= 0) seconds = 1;
    107    ::sleep(seconds);
    108 }
    109 
    110 
    111 void ThreadFuncs::startThread(ThreadFunc func, void *param)
    112 {
    113     unsigned long x;
    114 
    115     pthread_t tId;
    116     //thread_t tId;
    117 #if defined(_HP_UX) && defined(XML_USE_DCE)
    118     x = pthread_create( &tId, pthread_attr_default,  (pthreadfunc)func,  param);
    119 #else
    120     pthread_attr_t attr;
    121     pthread_attr_init(&attr);
    122     x = pthread_create( &tId, &attr,  (pthreadfunc)func,  param);
    123 #endif
    124     if (x == -1)
    125     {
    126         fprintf(stderr, "Error starting thread.  Errno = %d\n", errno);
    127         exit(-1);
    128     }
    129 }
    130 
    131 unsigned long ThreadFuncs::getCurrentMillis() {
    132     timeb aTime;
    133     ftime(&aTime);
    134     return (unsigned long)(aTime.time*1000 + aTime.millitm);
    135 }
    136 }
    137 
    138 
    139 // #else
    140 // #error This platform is not supported
    141 #endif
    142 
    143 
    144 
    145 //------------------------------------------------------------------------------
    146 //
    147 //  struct runInfo     Holds the info extracted from the command line and data
    148 //                     that is shared by all threads.
    149 //                     There is only one of these, and it is static.
    150 //                     During the test, the threads will access this info without
    151 //                     any synchronization.
    152 //
    153 //------------------------------------------------------------------------------
    154 const int MAXINFILES = 25;
    155 struct RunInfo
    156 {
    157     bool                quiet;
    158     bool                verbose;
    159     int                 numThreads;
    160     int                 totalTime;
    161     int                 checkTime;
    162     AbstractThreadTest *fTest;
    163     bool                stopFlag;
    164     bool                exitFlag;
    165     int32_t             runningThreads;
    166 };
    167 
    168 
    169 //------------------------------------------------------------------------------
    170 //
    171 //  struct threadInfo  Holds information specific to an individual thread.
    172 //                     One of these is set up for each thread in the test.
    173 //                     The main program monitors the threads by looking
    174 //                     at the status stored in these structs.
    175 //
    176 //------------------------------------------------------------------------------
    177 struct ThreadInfo
    178 {
    179     bool    fHeartBeat;            // Set true by the thread each time it finishes
    180                                    //   a test.
    181     unsigned int     fCycles;      // Number of cycles completed.
    182     int              fThreadNum;   // Identifying number for this thread.
    183     ThreadInfo() {
    184         fHeartBeat = false;
    185         fCycles = 0;
    186         fThreadNum = -1;
    187     }
    188 };
    189 
    190 
    191 //
    192 //------------------------------------------------------------------------------
    193 //
    194 //  Global Data
    195 //
    196 //------------------------------------------------------------------------------
    197 RunInfo         gRunInfo;
    198 ThreadInfo      *gThreadInfo;
    199 UMTX            gStopMutex;        // Lets main thread suspend test threads.
    200 UMTX            gInfoMutex;        // Synchronize access to data passed between
    201                                    //  worker threads and the main thread
    202 
    203 
    204 //----------------------------------------------------------------------
    205 //
    206 //   parseCommandLine   Read through the command line, and save all
    207 //                      of the options in the gRunInfo struct.
    208 //
    209 //                      Display the usage message if the command line
    210 //                      is no good.
    211 //
    212 //                      Probably ought to be a member function of RunInfo.
    213 //
    214 //----------------------------------------------------------------------
    215 
    216 void parseCommandLine(int argc, char **argv)
    217 {
    218     gRunInfo.quiet = false;               // Set up defaults for run.
    219     gRunInfo.verbose = false;
    220     gRunInfo.numThreads = 2;
    221     gRunInfo.totalTime = 0;
    222     gRunInfo.checkTime = 10;
    223 
    224     try             // Use exceptions for command line syntax errors.
    225     {
    226         int argnum = 1;
    227         while (argnum < argc)
    228         {
    229             if      (strcmp(argv[argnum], "-quiet") == 0)
    230                 gRunInfo.quiet = true;
    231             else if (strcmp(argv[argnum], "-verbose") == 0)
    232                 gRunInfo.verbose = true;
    233             else if (strcmp(argv[argnum], "--help") == 0 ||
    234                     (strcmp(argv[argnum],     "?")      == 0)) {throw 1; }
    235 
    236             else if (strcmp(argv[argnum], "-threads") == 0)
    237             {
    238                 ++argnum;
    239                 if (argnum >= argc)
    240                     throw 1;
    241                 gRunInfo.numThreads = atoi(argv[argnum]);
    242                 if (gRunInfo.numThreads < 0)
    243                     throw 1;
    244             }
    245             else if (strcmp(argv[argnum], "-time") == 0)
    246             {
    247                 ++argnum;
    248                 if (argnum >= argc)
    249                     throw 1;
    250                 gRunInfo.totalTime = atoi(argv[argnum]);
    251                 if (gRunInfo.totalTime < 1)
    252                     throw 1;
    253             }
    254             else if (strcmp(argv[argnum], "-ctime") == 0)
    255             {
    256                 ++argnum;
    257                 if (argnum >= argc)
    258                     throw 1;
    259                 gRunInfo.checkTime = atoi(argv[argnum]);
    260                 if (gRunInfo.checkTime < 1)
    261                     throw 1;
    262             }
    263             else if (strcmp(argv[argnum], "string") == 0)
    264             {
    265                 gRunInfo.fTest = createStringTest();
    266             }
    267             else if (strcmp(argv[argnum], "convert") == 0)
    268             {
    269                 gRunInfo.fTest = createConvertTest();
    270             }
    271            else
    272             {
    273                 fprintf(stderr, "Unrecognized command line option.  Scanning \"%s\"\n",
    274                     argv[argnum]);
    275                 throw 1;
    276             }
    277             argnum++;
    278         }
    279         // We've reached the end of the command line parameters.
    280         // Fail if no test name was specified.
    281         if (gRunInfo.fTest == NULL) {
    282             fprintf(stderr, "No test specified.\n");
    283             throw 1;
    284         }
    285 
    286     }
    287     catch (int)
    288     {
    289         fprintf(stderr, "usage:  threadtest [-threads nnn] [-time nnn] [-quiet] [-verbose] test-name\n"
    290             "     -quiet         Suppress periodic status display. \n"
    291             "     -verbose       Display extra messages. \n"
    292             "     -threads nnn   Number of threads.  Default is 2. \n"
    293             "     -time nnn      Total time to run, in seconds.  Default is forever.\n"
    294             "     -ctime nnn     Time between extra consistency checks, in seconds.  Default 10\n"
    295             "     testname       string | convert\n"
    296             );
    297         exit(1);
    298     }
    299 }
    300 
    301 
    302 
    303 
    304 
    305 //----------------------------------------------------------------------
    306 //
    307 //  threadMain   The main function for each of the swarm of test threads.
    308 //               Run in a loop, executing the runOnce() test function each time.
    309 //
    310 //
    311 //----------------------------------------------------------------------
    312 
    313 extern "C" {
    314 
    315 void threadMain (void *param)
    316 {
    317     ThreadInfo   *thInfo = (ThreadInfo *)param;
    318 
    319     if (gRunInfo.verbose)
    320         printf("Thread #%d: starting\n", thInfo->fThreadNum);
    321     umtx_atomic_inc(&gRunInfo.runningThreads);
    322 
    323     //
    324     //
    325     while (true)
    326     {
    327         if (gRunInfo.verbose )
    328             printf("Thread #%d: starting loop\n", thInfo->fThreadNum);
    329 
    330         //
    331         //  If the main thread is asking us to wait, do so by locking gStopMutex
    332         //     which will block us, since the main thread will be holding it already.
    333         //
    334         umtx_lock(&gInfoMutex);
    335         UBool stop = gRunInfo.stopFlag;  // Need mutex for processors with flakey memory models.
    336         umtx_unlock(&gInfoMutex);
    337 
    338         if (stop) {
    339             if (gRunInfo.verbose) {
    340                 fprintf(stderr, "Thread #%d: suspending\n", thInfo->fThreadNum);
    341             }
    342             umtx_atomic_dec(&gRunInfo.runningThreads);
    343             while (gRunInfo.stopFlag) {
    344                 umtx_lock(&gStopMutex);
    345                 umtx_unlock(&gStopMutex);
    346             }
    347             umtx_atomic_inc(&gRunInfo.runningThreads);
    348             if (gRunInfo.verbose) {
    349                 fprintf(stderr, "Thread #%d: restarting\n", thInfo->fThreadNum);
    350             }
    351         }
    352 
    353         //
    354         // The real work of the test happens here.
    355         //
    356         gRunInfo.fTest->runOnce();
    357 
    358         umtx_lock(&gInfoMutex);
    359         thInfo->fHeartBeat = true;
    360         thInfo->fCycles++;
    361         UBool exitNow = gRunInfo.exitFlag;
    362         umtx_unlock(&gInfoMutex);
    363 
    364         //
    365         // If main thread says it's time to exit, break out of the loop.
    366         //
    367         if (exitNow) {
    368             break;
    369         }
    370     }
    371 
    372     umtx_atomic_dec(&gRunInfo.runningThreads);
    373 
    374     // Returning will kill the thread.
    375     return;
    376 }
    377 
    378 }
    379 
    380 
    381 
    382 
    383 //----------------------------------------------------------------------
    384 //
    385 //   main
    386 //
    387 //----------------------------------------------------------------------
    388 
    389 int main (int argc, char **argv)
    390 {
    391     //
    392     //  Parse the command line options, and create the specified kind of test.
    393     //
    394     parseCommandLine(argc, argv);
    395 
    396 
    397     //
    398     //  Fire off the requested number of parallel threads
    399     //
    400 
    401     if (gRunInfo.numThreads == 0)
    402         exit(0);
    403 
    404     gRunInfo.exitFlag = FALSE;
    405     gRunInfo.stopFlag = TRUE;      // Will cause the new threads to block
    406     umtx_lock(&gStopMutex);
    407 
    408     gThreadInfo = new ThreadInfo[gRunInfo.numThreads];
    409     int threadNum;
    410     for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
    411     {
    412         gThreadInfo[threadNum].fThreadNum = threadNum;
    413         ThreadFuncs::startThread(threadMain, &gThreadInfo[threadNum]);
    414     }
    415 
    416 
    417     unsigned long startTime = ThreadFuncs::getCurrentMillis();
    418     int elapsedSeconds = 0;
    419     int timeSinceCheck = 0;
    420 
    421     //
    422     // Unblock the threads.
    423     //
    424     gRunInfo.stopFlag = FALSE;       // Unblocks the worker threads.
    425     umtx_unlock(&gStopMutex);
    426 
    427     //
    428     //  Loop, watching the heartbeat of the worker threads.
    429     //    Each second,
    430     //            display "+" if all threads have completed at least one loop
    431     //            display "." if some thread hasn't since previous "+"
    432     //    Each "ctime" seconds,
    433     //            Stop all the worker threads at the top of their loop, then
    434     //            call the test's check function.
    435     //
    436     while (gRunInfo.totalTime == 0 || gRunInfo.totalTime > elapsedSeconds)
    437     {
    438         ThreadFuncs::Sleep(1000);      // We sleep while threads do their work ...
    439 
    440         if (gRunInfo.quiet == false && gRunInfo.verbose == false)
    441         {
    442             char c = '+';
    443             int threadNum;
    444             umtx_lock(&gInfoMutex);
    445             for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
    446             {
    447                 if (gThreadInfo[threadNum].fHeartBeat == false)
    448                 {
    449                     c = '.';
    450                     break;
    451                 };
    452             }
    453             umtx_unlock(&gInfoMutex);
    454             fputc(c, stdout);
    455             fflush(stdout);
    456             if (c == '+')
    457                 for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++)
    458                     gThreadInfo[threadNum].fHeartBeat = false;
    459         }
    460 
    461         //
    462         // Update running times.
    463         //
    464         timeSinceCheck -= elapsedSeconds;
    465         elapsedSeconds = (ThreadFuncs::getCurrentMillis() - startTime) / 1000;
    466         timeSinceCheck += elapsedSeconds;
    467 
    468         //
    469         //  Call back to the test to let it check its internal validity
    470         //
    471         if (timeSinceCheck >= gRunInfo.checkTime) {
    472             if (gRunInfo.verbose) {
    473                 fprintf(stderr, "Main: suspending all threads\n");
    474             }
    475             umtx_lock(&gStopMutex);               // Block the worker threads at the top of their loop
    476             gRunInfo.stopFlag = TRUE;
    477             for (;;) {
    478                 umtx_lock(&gInfoMutex);
    479                 UBool done = gRunInfo.runningThreads == 0;
    480                 umtx_unlock(&gInfoMutex);
    481                 if (done) { break;}
    482                 ThreadFuncs::yield();
    483             }
    484 
    485 
    486 
    487             gRunInfo.fTest->check();
    488             if (gRunInfo.quiet == false && gRunInfo.verbose == false) {
    489                 fputc('C', stdout);
    490             }
    491 
    492             if (gRunInfo.verbose) {
    493                 fprintf(stderr, "Main: starting all threads.\n");
    494             }
    495             gRunInfo.stopFlag = FALSE;       // Unblock the worker threads.
    496             umtx_unlock(&gStopMutex);
    497             timeSinceCheck = 0;
    498         }
    499     };
    500 
    501     //
    502     //  Time's up, we are done.  (We only get here if this was a timed run)
    503     //  Tell the threads to exit.
    504     //
    505     gRunInfo.exitFlag = true;
    506     for (;;) {
    507         umtx_lock(&gInfoMutex);
    508         UBool done = gRunInfo.runningThreads == 0;
    509         umtx_unlock(&gInfoMutex);
    510         if (done) { break;}
    511         ThreadFuncs::yield();
    512     }
    513 
    514     //
    515     //  Tally up the total number of cycles completed by each of the threads.
    516     //
    517     double totalCyclesCompleted = 0;
    518     for (threadNum=0; threadNum < gRunInfo.numThreads; threadNum++) {
    519         totalCyclesCompleted += gThreadInfo[threadNum].fCycles;
    520     }
    521 
    522     double cyclesPerMinute = totalCyclesCompleted / (double(gRunInfo.totalTime) / double(60));
    523     printf("\n%8.1f cycles per minute.", cyclesPerMinute);
    524 
    525     //
    526     //  Memory should be clean coming out
    527     //
    528     delete gRunInfo.fTest;
    529     delete [] gThreadInfo;
    530     umtx_destroy(&gInfoMutex);
    531     umtx_destroy(&gStopMutex);
    532     u_cleanup();
    533 
    534     return 0;
    535 }
    536 
    537 
    538