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