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