OLD | NEW |
| (Empty) |
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 withou
t | |
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 finish
es | |
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 fore
ver.\n" | |
294 " -ctime nnn Time between extra consistency checks, in secon
ds. 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 alr
eady. | |
333 // | |
334 umtx_lock(&gInfoMutex); | |
335 UBool stop = gRunInfo.stopFlag; // Need mutex for processors with flake
y 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 | |
OLD | NEW |