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
19AbstractThreadTest::~AbstractThreadTest() {}
20
21//------------------------------------------------------------------------------
22//
23//   Factory functions for creating different test types.
24//
25//------------------------------------------------------------------------------
26extern  AbstractThreadTest *createStringTest();
27extern  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
43typedef void (*ThreadFunc)(void *);
44
45class ThreadFuncs           // This class isolates OS dependent threading
46{                           //   functions from the rest of ThreadTest program.
47public:
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
54void 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
65unsigned 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
88extern "C" {
89
90
91typedef void (*ThreadFunc)(void *);
92typedef void *(*pthreadfunc)(void *);
93
94class ThreadFuncs           // This class isolates OS dependent threading
95{                           //   functions from the rest of ThreadTest program.
96public:
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
103void ThreadFuncs::Sleep(int millis)
104{
105   int seconds = millis/1000;
106   if (seconds <= 0) seconds = 1;
107   ::sleep(seconds);
108}
109
110
111void 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
131unsigned 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//------------------------------------------------------------------------------
154const int MAXINFILES = 25;
155struct 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//------------------------------------------------------------------------------
177struct 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//------------------------------------------------------------------------------
197RunInfo         gRunInfo;
198ThreadInfo      *gThreadInfo;
199UMTX            gStopMutex;        // Lets main thread suspend test threads.
200UMTX            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
216void 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
313extern "C" {
314
315void 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
389int 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
538