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:
Sleep(int millis)47 static void Sleep(int millis) {::Sleep(millis);};
48 static void startThread(ThreadFunc, void *param);
49 static unsigned long getCurrentMillis();
yield()50 static void yield() {::Sleep(0);};
51 };
52
startThread(ThreadFunc func,void * param)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
getCurrentMillis()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();
yield()99 static void yield() {sched_yield();};
100 };
101
Sleep(int millis)102 void ThreadFuncs::Sleep(int millis)
103 {
104 int seconds = millis/1000;
105 if (seconds <= 0) seconds = 1;
106 ::sleep(seconds);
107 }
108
109
startThread(ThreadFunc func,void * param)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
getCurrentMillis()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.
ThreadInfoThreadInfo182 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
parseCommandLine(int argc,char ** argv)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
threadMain(void * param)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
main(int argc,char ** argv)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