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//------------------------------------------------------------------------------ 25extern AbstractThreadTest *createStringTest(); 26extern 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 42typedef void (*ThreadFunc)(void *); 43 44class ThreadFuncs // This class isolates OS dependent threading 45{ // functions from the rest of ThreadTest program. 46public: 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 53void 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 64unsigned 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 87extern "C" { 88 89 90typedef void (*ThreadFunc)(void *); 91typedef void *(*pthreadfunc)(void *); 92 93class ThreadFuncs // This class isolates OS dependent threading 94{ // functions from the rest of ThreadTest program. 95public: 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 102void ThreadFuncs::Sleep(int millis) 103{ 104 int seconds = millis/1000; 105 if (seconds <= 0) seconds = 1; 106 ::sleep(seconds); 107} 108 109 110void 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 130unsigned 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//------------------------------------------------------------------------------ 153const int MAXINFILES = 25; 154struct 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//------------------------------------------------------------------------------ 176struct 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//------------------------------------------------------------------------------ 196RunInfo gRunInfo; 197ThreadInfo *gThreadInfo; 198UMTX gStopMutex; // Lets main thread suspend test threads. 199UMTX 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 215void 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 312extern "C" { 313 314void 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 388int 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