• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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