Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / icu / source / 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