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//------------------------------------------------------------------------------
25extern  AbstractThreadTest *createStringTest();
26extern  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
42typedef void (*ThreadFunc)(void *);
43
44class ThreadFuncs           // This class isolates OS dependent threading
45{                           //   functions from the rest of ThreadTest program.
46public:
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
53void 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
64unsigned 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
87extern "C" {
88
89
90typedef void (*ThreadFunc)(void *);
91typedef void *(*pthreadfunc)(void *);
92
93class ThreadFuncs           // This class isolates OS dependent threading
94{                           //   functions from the rest of ThreadTest program.
95public:
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
102void ThreadFuncs::Sleep(int millis)
103{
104   int seconds = millis/1000;
105   if (seconds <= 0) seconds = 1;
106   ::sleep(seconds);
107}
108
109
110void 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
130unsigned 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//------------------------------------------------------------------------------
153const int MAXINFILES = 25;
154struct 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//------------------------------------------------------------------------------
176struct 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//------------------------------------------------------------------------------
196RunInfo         gRunInfo;
197ThreadInfo      *gThreadInfo;
198UMTX            gStopMutex;        // Lets main thread suspend test threads.
199UMTX            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
215void 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
312extern "C" {
313
314void 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
388int 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