7891a888f19205de76e556f24ad147102a01f895
[platform/framework/web/crosswalk.git] / src / third_party / icu / source / test / threadtest / threadtest.cpp
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:
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
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
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();
99     static void            yield() {sched_yield();};
100 };
101
102 void ThreadFuncs::Sleep(int millis)
103 {
104    int seconds = millis/1000;
105    if (seconds <= 0) seconds = 1;
106    ::sleep(seconds);
107 }
108
109
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
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.
182     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
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
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
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