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