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 19AbstractThreadTest::~AbstractThreadTest() {} 20 21//------------------------------------------------------------------------------ 22// 23// Factory functions for creating different test types. 24// 25//------------------------------------------------------------------------------ 26extern AbstractThreadTest *createStringTest(); 27extern 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 43typedef void (*ThreadFunc)(void *); 44 45class ThreadFuncs // This class isolates OS dependent threading 46{ // functions from the rest of ThreadTest program. 47public: 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 54void 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 65unsigned 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 88extern "C" { 89 90 91typedef void (*ThreadFunc)(void *); 92typedef void *(*pthreadfunc)(void *); 93 94class ThreadFuncs // This class isolates OS dependent threading 95{ // functions from the rest of ThreadTest program. 96public: 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 103void ThreadFuncs::Sleep(int millis) 104{ 105 int seconds = millis/1000; 106 if (seconds <= 0) seconds = 1; 107 ::sleep(seconds); 108} 109 110 111void 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 131unsigned 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//------------------------------------------------------------------------------ 154const int MAXINFILES = 25; 155struct 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//------------------------------------------------------------------------------ 177struct 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//------------------------------------------------------------------------------ 197RunInfo gRunInfo; 198ThreadInfo *gThreadInfo; 199UMTX gStopMutex; // Lets main thread suspend test threads. 200UMTX 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 216void 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 313extern "C" { 314 315void 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 389int 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