]> git.saurik.com Git - apple/icu.git/blob - icuSources/test/threadtest/threadtest.cpp
ICU-491.11.1.tar.gz
[apple/icu.git] / icuSources / test / threadtest / threadtest.cpp
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 without
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 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 //------------------------------------------------------------------------------
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 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
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 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
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