2 * The Regina Rexx Interpreter
3 * Copyright (C) 2001-2004 Florian Große-Coosmann
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 ******************************************************************************
21 * Asynchroneous thread multiplexer with a parallel use of the REXXSAA API.
23 * This example works with Win32 as with OS/2 or Posix threads.
33 # include <sys/time.h>
36 # define USE_SEMAPHORES 0
37 # ifdef _POSIX_SEMAPHORES
38 # include <semaphore.h>
40 # undef USE_SEMAPHORES
41 # define USE_SEMAPHORES 1
49 # define INCL_DOSSEMAPHORES
52 # define CHAR_TYPEDEFED
53 # define SHORT_TYPEDEFED
54 # define LONG_TYPEDEFED
56 # define _OS2EMX_H /* prevents PFN from defining (Watcom) */
61 /* This picky compiler claims about unused formal parameters.
62 * This is correct but hides (for human eyes) other errors since they
63 * are many and we can't reduce them all.
64 * Error 4100 is "unused formal parameter".
66 # pragma warning(disable:4100 4115 4201 4214 4514)
70 # define WIN32_LEAN_AND_MEAN
77 # pragma warning(default:4100 4115 4201 4214)
82 #define INCL_RXSYSEXIT
92 * MAX_THREADS is the number of parallel starting threads. 20 is a good
95 #define MAX_THREADS 20
98 * MAX_RUN defines the number of loops each thread has to perform.
101 #define MAX_RUN ( sizeof( unsigned ) * CHAR_BIT )
104 * TOTAL_THREADS is the number of threads which shall be created. 2000 should
105 * be sufficient to detect memory leaks, etc.
106 * Can be overwritten with -t command line switch
108 #define TOTAL_THREADS 1500
109 int total_threads
= TOTAL_THREADS
;
112 * timeout_seconds is the number of seconds a thread is allowed to live before
113 * stopping the program. Overwrite with -s command line switch
115 int timeout_seconds
= 3;
119 * See below at WIN32_THREADS for a description.
121 #define ThreadIndexType pthread_t
122 #define my_threadidx() pthread_self()
123 #define my_threadid() pthread_self()
124 #define THREAD_RETURN void *
125 #define THREAD_CONVENTION
126 static pthread_t thread
[MAX_THREADS
];
131 * See below at WIN32_THREADS for a description.
133 #define ThreadIndexType int
134 #define my_threadidx() *_threadid
135 #define my_threadid() *_threadid
136 #define THREAD_RETURN void
137 #define THREAD_RETURN_VOID 1
138 #define THREAD_CONVENTION
139 static int thread
[MAX_THREADS
];
144 * ThreadIndexType is the type of my_threadidx which shall be the thread
147 #define ThreadIndexType DWORD
150 * my_threadidx() has to return the thread identifier of the current thread.
152 #define my_threadidx() GetCurrentThreadId()
155 * my_threadid() has to return the thread's handle of the current thread if
156 * such a thing exists.
158 #define my_threadid() GetCurrentThread()
161 * THREAD_RETURN defines the return value type of the thread creation function.
163 #define THREAD_RETURN unsigned
166 * THREAD_CONVENTION defines the calling convention of the thread creation
167 * function. It may be the empty string for cdecl.
169 #define THREAD_CONVENTION __stdcall
172 * thread will hold the handle of each thread.
174 static HANDLE thread
[MAX_THREADS
];
179 * threadx will hold the identifier of each thread (in opposite to "thread").
181 static ThreadIndexType threadx
[MAX_THREADS
];
184 * State manages the state of each possible parallel thread in the arrays
185 * "thread" and "threadx".
188 Ready
= 0, /* The thread's slot may be used */
189 Running
, /* Thread has been started */
190 Stopped
/* This value is used by a thread to signal its death. */
191 } State
[MAX_THREADS
];
194 * found contains the number of properly detected lines by the thread.
195 * Lines matching a pattern are expected. The values are used bitwise.
197 static unsigned found
[MAX_THREADS
];
200 * GlobalError contains the global error code (and return value).
201 * This variables type shall be threadsafe.
203 static int GlobalError
= 0;
206 * stdout_is_tty is a flag which is set when we may drop additional garbage
209 static int stdout_is_tty
= 0;
212 * UseInstore manages the state of the instore-processing. Instore macros
213 * are precompiled macros and shall run faster than normal ones.
216 UnUsed
= 0, /* Don't use instore macros at all. */
217 FirstRun
, /* This is the first run when compiling the macros */
218 DoInstore
/* We have to use the instore macro */
219 } UseInstore
= UnUsed
;
222 * InstoreBuf will hold the compiled macro.
224 static void *InstoreBuf
= NULL
;
227 * InstoreLen will hold the compiled macro's length if InstoreBuf is non-NULL.
229 static unsigned InstoreLen
;
231 static void ThreadHasStopped(unsigned position
);
233 static char *program_name
= NULL
;
236 * We redirect Rexx' output. This is the redirection handler.
237 * We expect the string "Loop <x> in thread <y>" where x is a running
238 * counter and y is the thread identifier returned by Regina.
240 LONG APIENTRY
instore_exit( LONG ExNum
, LONG Subfun
, PEXIT PBlock
)
242 RXSIOSAY_PARM
*psiosay
;
243 char buf
[256]; /* enough to hold the data */
245 unsigned len
,tid
,loop
;
248 if ( ( ExNum
!= RXSIO
) || ( Subfun
!= RXSIOSAY
) ) /* unexpected? */
249 return RXEXIT_NOT_HANDLED
;
252 return RXEXIT_HANDLED
;
255 * We have to check the data after fetching it from the parameter block.
257 psiosay
= ( RXSIOSAY_PARM
* ) PBlock
;
258 rx
= psiosay
->rxsio_string
;
259 if ( !RXVALIDSTRING( rx
) )
261 fprintf( stderr
, "\n"
262 "Thread %lu gives an invalid string for a SAY output.\n",
263 ( unsigned long ) my_threadidx() );
265 return RXEXIT_HANDLED
;
268 len
= RXSTRLEN( rx
);
269 if ( len
>= sizeof(buf
) )
270 len
= sizeof(buf
) - 1; /* This shall NOT happen, but it's irrelevant */
271 memcpy( buf
, RXSTRPTR( rx
), len
);
272 buf
[len
] = '\0'; /* We have a sscanf-able string */
273 rc
= sscanf( buf
, "Loop %u in thread %u", &loop
, &tid
);
276 fprintf( stderr
, "\n"
277 "Thread %lu gives an unexpected SAY output \"%s\".\n",
278 ( unsigned long ) my_threadidx(), buf
);
280 return RXEXIT_HANDLED
;
284 * Is the wrong thread addressed? This may be a common error.
286 if ( ( ThreadIndexType
) tid
!= my_threadidx() )
288 fprintf( stderr
, "\n"
289 "Thread %lu claims an incorrect thread identifier %lu.\n",
290 ( unsigned long ) my_threadidx(),
291 ( unsigned long ) tid
);
293 return RXEXIT_HANDLED
;
297 * Is this a known thread?
299 for ( idx
= 0; idx
< MAX_THREADS
; idx
++ )
301 if ( threadx
[idx
] == ( ThreadIndexType
) tid
)
305 if ( idx
>= MAX_THREADS
)
307 fprintf( stderr
, "\n"
308 "Thread %lu can't be found in the thread table.\n",
309 ( unsigned long ) my_threadidx() );
311 return RXEXIT_HANDLED
;
315 * Check the loop number. We may have lost or duplicated data.
318 if ( ( loop
< 1 ) || ( loop
> MAX_RUN
) )
322 loop
= 1 << ( loop
- 1 ); /* Bitmask for the loop */
323 if ( found
[idx
] & loop
)
324 rc
= 1; /* already found */
330 fprintf( stderr
, "\n"
331 "Thread %lu's loop doesn't run continuously.\n",
332 ( unsigned long ) my_threadidx() );
336 return RXEXIT_HANDLED
;
340 * instore is a separate thread and invokes a Rexx script.
341 * It runs a loop (inside Rexx) and checks for errors.
342 * The return value is 0 if a return value is used at all.
343 * The argument data is the index of the thread within threadx.
345 THREAD_RETURN THREAD_CONVENTION
instore( void *data
)
349 char instore_buf
[256];
353 * signal that we are alive.
355 threadx
[(unsigned) data
] = my_threadidx();
358 * Register an exit handler which shall check Regina's output of the thread.
360 RexxRegisterExitExe( "ExitHandler", /* name of the handler */
362 (PFN
) instore_exit
, /* entry point of the handler */
364 instore_exit
, /* entry point of the handler */
366 NULL
); /* user area of the handler */
369 * Build up the structure which informs Regina to use the exit handler.
371 Exits
[0].sysexit_name
= "ExitHandler";
372 Exits
[0].sysexit_code
= RXSIO
;
373 Exits
[1].sysexit_code
= RXENDLST
;
375 sprintf( instore_buf
, "Do i = 1 To %u;"
376 "say 'Loop' i 'in thread' gettid();"
379 (unsigned) MAX_RUN
);
380 Instore
[0].strptr
= instore_buf
;
381 Instore
[0].strlength
= strlen( Instore
[0].strptr
);
382 if ( UseInstore
== DoInstore
)
385 * We don't need a script any longer. Instead we pass a compiled script
386 * for the interpreter. You may or may not pass the original script,
389 Instore
[1].strptr
= (char *)InstoreBuf
;
390 Instore
[1].strlength
= InstoreLen
;
394 Instore
[1].strptr
= NULL
;
396 rc
= RexxStart( 0, /* ArgCount */
398 "Testing", /* ProgramName */
399 Instore
, /* Instore (source/compiled) */
400 "Foo", /* EnvironmentName */
401 RXCOMMAND
, /* CallType */
402 Exits
, /* ExitHandlerList */
403 NULL
, /* ReturnCode (ignored) */
404 NULL
); /* ReturnValue (ignored) */
405 switch ( UseInstore
)
409 * We will get an instore macro even if not desired. Delete it.
411 if ( Instore
[1].strptr
)
412 RexxFreeMemory( Instore
[1].strptr
);
416 fprintf( stderr
, "\n"
417 "Didn't got the instore macro.\n" );
423 * On the first run save the instore macro for later use.
425 if ( Instore
[1].strptr
)
427 InstoreBuf
= Instore
[1].strptr
;
428 InstoreLen
= Instore
[1].strlength
;
433 fprintf( stderr
, "\n"
434 "Didn't got the instore macro.\n" );
440 * I don't know if the standard allows a success and a return value
441 * of NULL in Instore[1]. Ignore it. It will be detected later.
442 * True application should check the return code of RexxStart().
446 RexxDeregisterExit( "ExitHandler", /* name of the handler */
447 NULL
); /* module name, NULL=executable */
450 * Finally inform the invoker that we have stopped gracefully.
452 ThreadHasStopped( ( unsigned ) data
);
456 #ifndef THREAD_RETURN_VOID
457 return ( THREAD_RETURN
) 0;
462 * external is a separate thread and invokes a Rexx program from disk.
463 * It runs a loop (inside Rexx) and checks for errors.
464 * The return value is 0 if a return value is used at all.
465 * The argument data is the index of the thread within threadx.
466 * The Rexx program filename is in a global variable.
468 THREAD_RETURN THREAD_CONVENTION
external( void *data
)
473 * signal that we are alive.
475 threadx
[(unsigned) data
] = my_threadidx();
477 rc
= RexxStart( 0, /* ArgCount */
479 program_name
, /* ProgramName */
480 NULL
, /* Instore (source/compiled) */
481 "Foo", /* EnvironmentName */
482 RXCOMMAND
, /* CallType */
483 NULL
, /* ExitHandlerList */
484 NULL
, /* ReturnCode (ignored) */
485 NULL
); /* ReturnValue (ignored) */
488 * Finally inform the invoker that we have stopped gracefully.
490 ThreadHasStopped( ( unsigned ) data
);
494 #ifndef THREAD_RETURN_VOID
495 return ( THREAD_RETURN
) 0;
499 /******************************************************************************
500 ******************************************************************************
501 * thread management **********************************************************
502 ******************************************************************************
503 *****************************************************************************/
506 * reap checks the thread's result buffer to see if it has seen all the lines
507 * the interpreter has emitted.
508 * The global error is set in case of an error.
509 * The result buffer is reset on success.
510 * Only called when using "instore" macro.
512 void reap( unsigned position
)
514 if (found
[position
] != ~((unsigned) 0))
517 "Thread %lu has stopped without completing its loop.\n",
518 (unsigned long) threadx
[position
]);
526 * init_threads initializes the usage of our thread management system.
527 * Returns 1 on success, 0 otherwise.
529 int init_threads( void )
535 * start_a_thread starts a thread and sets some state informations which are
536 * set back in case of an error.
537 * The return code is 1 on success, 0 otherwise.
539 int start_a_thread( unsigned position
)
544 State
[position
] = Running
;
545 /* Avoid some race conditions. I don't know if this is a problem of the
546 * runtime system or the kernel. If the systems runs into severe swapping
547 * the threads seems to run before the thread id is known which is used
548 * in instore_exit. We suspend the thread until all details of the new
549 * thread are known before we continue. This gives a little bit worse
552 thread
[position
] = ( HANDLE
) _beginthreadex( NULL
,
554 (program_name
) ? external
: instore
,
558 rc
= ( long ) thread
[position
] != 0l;
561 fprintf( stderr
, "\n"
562 "Error starting thread, error code is %ld\n",
565 ThreadHasStopped( position
);
567 ResumeThread( thread
[position
] );
572 * Thread has stopped sets the global state information of the thread with the
573 * index "position" to "Stopped".
575 static void ThreadHasStopped( unsigned position
)
577 State
[position
] = Stopped
;
581 * wait_for_threads restarts new threads until the requested count of
582 * TOTAL_THREADS has been reached. GlobalError is set if any error occurs.
584 * We expect to have MAX_THREADS already running.
586 void wait_for_threads( void )
588 unsigned i
,j
,done
,running
;
590 HANDLE compressed
[MAX_THREADS
];
591 unsigned BackSort
[MAX_THREADS
];
593 running
= done
= MAX_THREADS
;
596 printf( "%u\r", MAX_THREADS
);
603 * We have to pass an array of thread handles to the OS' waiter function.
604 * But some threads may not be running at the last steps. Therefore we
605 * have to resort the handles to be consecutive in a temporary array.
607 for ( i
= 0, j
= 0; i
< MAX_THREADS
; i
++ )
609 if ( State
[i
] != Ready
)
611 compressed
[j
] = thread
[i
];
616 rc
= WaitForMultipleObjects( running
, compressed
, FALSE
, timeout_seconds
*1000 );
617 if ( rc
== 0xFFFFFFFF )
620 * Failure or dead thread, look for a stopped one
622 for ( i
= 0; i
< running
; i
++ )
624 if ( State
[BackSort
[i
]] == Stopped
)
625 rc
= WAIT_OBJECT_0
+ i
;
628 if ( ( rc
< WAIT_OBJECT_0
) || ( rc
>= running
+ WAIT_OBJECT_0
) )
630 fprintf( stderr
, "\n"
631 "At least one thread won't finish normally within 3 seconds (rc=%u, error=%lu).\n",
632 rc
, GetLastError() );
639 * A thread has died. Find it and check the reason.
641 i
= BackSort
[rc
- WAIT_OBJECT_0
];
642 if ( State
[i
] != Stopped
)
644 fprintf( stderr
, "\n"
645 "Thread %u hasn't finished normally.\n",i
);
651 * Destroy associated buffers, check values and
652 * restart a new instance if we still have to do so.
654 CloseHandle( thread
[i
] );
658 * Only reap our threads if running the instore test code
660 if ( program_name
== NULL
)
663 if ( done
< total_threads
)
665 if ( !start_a_thread( i
) )
671 printf( "%u(%u)\r", done
, running
);
672 if ( GlobalError
|| !running
)
680 * init_threads initializes the usage of our thread management system.
681 * Returns 1 on success, 0 otherwise.
684 SEMRECORD thread_sems
[MAX_THREADS
];
686 int init_threads( void )
691 for ( i
= 0; i
< MAX_THREADS
; i
++ )
693 thread_sems
[i
].ulUser
= i
;
694 rc
= DosCreateEventSem( NULL
,
695 (PHEV
) &thread_sems
[i
].hsemCur
,
700 fprintf( stderr
, "\n"
701 "Error creating an EventSem, error code is %lu\n",
707 rc
= DosCreateMuxWaitSem( NULL
,
714 fprintf( stderr
, "\n"
715 "Error creating a MuxWaitSem, error code is %lu\n",
723 * start_a_thread starts a thread and sets some state informations which are
724 * set back in case of an error.
725 * The return code is 1 on success, 0 otherwise.
727 int start_a_thread( unsigned position
)
731 rc
= DosResetEventSem( (HEV
) thread_sems
[position
].hsemCur
, &post
);
732 if ( ( rc
!= 0 ) && ( rc
!= ERROR_ALREADY_RESET
) )
734 fprintf( stderr
, "\n"
735 "Error resetting an EventSem, error code is %lu\n",
740 State
[position
] = Running
;
741 thread
[position
] = _beginthread( (program_name
) ? external
: instore
,
744 ( void * ) position
);
745 if ( thread
[position
] == -1 )
747 fprintf( stderr
, "\n"
748 "Error starting thread, error code is %d\n",
751 State
[position
] = Stopped
;
752 DosPostEventSem( (HEV
) thread_sems
[position
].hsemCur
);
759 * Thread has stopped sets the global state information of the thread with the
760 * index "position" to "Stopped".
762 static void ThreadHasStopped( unsigned position
)
766 State
[position
] = Stopped
;
767 if ( ( rc
= DosPostEventSem( (HEV
) thread_sems
[position
].hsemCur
) ) != 0 )
769 fprintf( stderr
, "\n"
770 "Error posting an EventSem, error code is %lu\n",
777 * wait_for_threads restarts new threads until the requested count of
778 * TOTAL_THREADS has been reached. GlobalError is set if any error occurs.
780 * We expect to have MAX_THREADS already running.
782 void wait_for_threads( void )
784 unsigned done
,running
;
785 ULONG rc
, post
, user
;
787 running
= done
= MAX_THREADS
;
790 printf( "%u\r", MAX_THREADS
);
796 rc
= DosWaitMuxWaitSem( hmux
, timeout_seconds
*1000, &user
);
799 fprintf( stderr
, "\n"
800 "At least one thread won't finish normally within 3 seconds (error=%lu).\n",
804 if ( user
>= MAX_THREADS
)
806 fprintf( stderr
, "\n"
807 "Strange behaviour after wating for MuxWaitSem, released thread index is %lu.\n",
815 * A thread has died. Check the reason.
817 if ( State
[user
] != Stopped
)
819 fprintf( stderr
, "\n"
820 "Thread %lu hasn't finished normally.\n", user
);
826 * Check values and restart a new instance if we still have to do so.
831 * Only reap our threads if running the instore test code
833 if ( program_name
== NULL
)
836 if ( done
< total_threads
)
838 if ( !start_a_thread( (int) user
) )
845 rc
= DosResetEventSem( (HEV
) thread_sems
[user
].hsemCur
, &post
);
846 if ( ( rc
!= 0 ) && ( rc
!= ERROR_ALREADY_RESET
) )
848 fprintf( stderr
, "\n"
849 "Error resetting an EventSem, error code is %lu\n",
856 printf( "%u(%u)\r", done
, running
);
857 if ( GlobalError
|| !running
)
865 * The number of processed runs needs to be global for error analysis.
867 static unsigned done
= 0;
870 static sem_t something_stopped
;
873 * timer_alarm will end the program if nothing happens within 3 seconds.
875 void timer_alarm( int sig
)
877 static unsigned lastvalue
= ( unsigned ) -1;
878 static int call_count
= 0;
880 if ( ( lastvalue
!= done
) || ( (int) done
== total_threads
) )
883 signal( SIGALRM
, timer_alarm
);
888 signal( SIGALRM
, timer_alarm
);
889 if ( call_count
++ == 0 )
891 fprintf( stderr
, "\n"
892 "At least one thread won't finish within 3 seconds.\n" );
893 sem_post( &something_stopped
);
896 if ( call_count
> 2 )
900 static pthread_mutex_t thread_lock
= PTHREAD_MUTEX_INITIALIZER
;
901 static pthread_cond_t something_stopped
= PTHREAD_COND_INITIALIZER
;
905 * init_threads initializes the usage of our thread management system.
906 * Returns 1 on success, 0 otherwise.
908 int init_threads( void )
911 if ( sem_init( &something_stopped
, 0, 0 ) != 0 )
918 * start_a_thread starts a thread and sets some state informations which are
919 * set back in case of an error.
920 * The return code is 1 on success, 0 otherwise.
922 int start_a_thread( unsigned position
)
926 State
[position
] = Running
;
927 rc
= pthread_create( &thread
[position
], NULL
, (program_name
) ? external
: instore
, (void *) position
);
930 fprintf( stderr
, "\n"
931 "Error starting thread, error code is %d\n",
934 ThreadHasStopped( position
);
941 * Thread has stopped sets the global state information of the thread with the
942 * index "position" to "Stopped".
943 * The master semaphore is set to signal the main thread about the thread's
946 static void ThreadHasStopped( unsigned position
)
949 State
[position
] = Stopped
;
950 sem_post( &something_stopped
);
953 * The use of the mutex lock semaphores is forced by Posix.
955 pthread_mutex_lock( &thread_lock
);
956 State
[position
] = Stopped
;
957 pthread_cond_signal( &something_stopped
);
958 pthread_mutex_unlock( &thread_lock
);
963 * wait_for_threads restarts new threads until the requested count of
964 * TOTAL_THREADS has been reached. GlobalError is set if any error occurs.
966 * We expect to have MAX_THREADS already running.
968 * Note: You will get a better performance if you set the schedule policy to
969 * RR or FIFO, but you have to be root to do this. If USE_SEMAPHORES is
970 * active, changing the policy won't increase the performance.
972 void wait_for_threads( void )
978 struct timespec timeout
;
980 struct itimerval ival
;
983 running
= done
= MAX_THREADS
;
985 printf( "%u\r", MAX_THREADS
);
992 * The lock is needed by pthread_cond_timewait.
993 * The multithreading paradigma here is very unefficient.
995 pthread_mutex_lock( &thread_lock
);
997 signal( SIGALRM
, timer_alarm
);
998 ival
.it_value
.tv_sec
= timeout_seconds
;
999 ival
.it_value
.tv_usec
= 0;
1000 ival
.it_interval
= ival
.it_value
;
1001 setitimer( ITIMER_REAL
, &ival
, NULL
);
1008 * Sleep a maximum of 3 seconds.
1010 gettimeofday(&now
,NULL
);
1011 timeout
.tv_sec
= now
.tv_sec
+ timeout_seconds
;
1012 timeout
.tv_nsec
= now
.tv_usec
* 1000;
1014 * The following call will wait up to timeout time for a signalled
1015 * something_stopped condition semaphore. thread_lock is atomically
1016 * unlocked on function entry and locked on function exit.
1018 rc
= pthread_cond_timedwait( &something_stopped
, &thread_lock
, &timeout
);
1019 if ( rc
== ETIMEDOUT
)
1021 fprintf( stderr
, "\n"
1022 "At least one thread won't finish within 3 seconds.\n" );
1026 if ( sem_wait( &something_stopped
) != 0 )
1028 fprintf( stderr
, "\n"
1029 "Interrupted wait.\n" );
1037 * Restart the threads is appropriate.
1039 for ( i
= 0; i
< MAX_THREADS
; i
++ )
1041 if ( State
[i
] != Stopped
)
1045 rc
= pthread_join( thread
[i
], NULL
);
1048 fprintf( stderr
, "\n"
1049 "A thread can't be found in the internal table.\n" );
1055 * Has the thread done its work completely?
1058 * Only reap our threads if running the instore test code
1060 if ( program_name
== NULL
)
1064 * Restart a new thread if we need some more runs.
1066 if ( (int) done
< total_threads
)
1068 if ( !start_a_thread( i
) )
1074 if ( stdout_is_tty
)
1075 printf( "%u(%u)\r", done
, running
);
1076 if ( GlobalError
|| !running
)
1080 pthread_mutex_unlock( &thread_lock
);
1086 * Usage shows this program's usage and stops the program.
1088 static void usage( void )
1090 printf( "usage: threader [-p] [-q] [-t total_threads] [-s timeout_seconds] [filename]\n"
1093 "-p\tLoad the macro only once and then use the generated instore\n"
1094 "\tmacro. Default: Always load the macro new.\n"
1096 "-q\tRun quietly. Don't display running progress information.\n"
1098 "-t\ttotal_threads\tTotal number of threads to execute.\n"
1100 "-s\ttimeoutseconds\tNumber of seconds to timeout a thread. Default 3.\n"
1102 "filename\tThe Rexx program to execute rather than the instore test\n"
1104 "\nThe default instore macro generates lines with numbers which can\n"
1105 "be parsed to detect problems in the multi-threading implementation.\n"
1106 "A loop counter runs by default until %u. The test should run from a few\n"
1107 "seconds up to a few minutes. You should hit ^C to abort the program\n"
1108 "if you think your harddisk is working heavily.\n"
1110 "This program is for testing Regina's multithreading capabilities.\n"
1115 int main( int argc
, char *argv
[] )
1123 * In case of a connected tty we let run a counter to show the user some
1126 if ( isatty( fileno( stdout
) ) )
1129 for ( i
= 1; i
< argc
; i
++ )
1131 if ( argv
[i
][0] != '-' )
1134 if ( strcmp( argv
[i
], "-p" ) == 0 )
1136 UseInstore
= FirstRun
;
1138 else if ( strcmp( argv
[i
], "-t" ) == 0 )
1141 total_threads
= atoi( argv
[i
] );
1143 else if ( strcmp( argv
[i
], "-s" ) == 0 )
1146 timeout_seconds
= atoi( argv
[i
] );
1148 else if ( strcmp( argv
[i
], "-q" ) == 0 )
1152 else if ( strcmp( argv
[i
], "--" ) == 0 )
1165 program_name
= argv
[i
];
1172 * Initialize some tables and tune the IO system to show every output
1175 memset( found
, 0,sizeof( found
) );
1176 memset( State
, 0,sizeof( State
) );
1177 setvbuf( stdout
,NULL
, _IONBF
, 0 );
1178 setvbuf( stderr
,NULL
, _IONBF
, 0 );
1179 printf( "Regina Rexx Thread Tester\n" );
1180 printf( "-------------------------\n" );
1182 version
.strlength
= 0;
1183 version
.strptr
= NULL
;
1185 * This will not work if we check another Rexx. You can safely comment out
1186 * the following code up to the 'printf( "\n" );'
1188 #ifdef REGINAVERSION
1189 versioncode
= ReginaVersion( &version
);
1190 printf( "Regina's version is %lu.%lu",
1192 versioncode
& 0xFF );
1193 if ( version
.strptr
)
1195 printf( " (in complete \"%s\")", version
.strptr
);
1196 RexxFreeMemory( version
.strptr
);
1201 if ( UseInstore
&& program_name
)
1203 printf( "Ignoring the \"-p\" flag for external REXX scripts\n" );
1204 UseInstore
= UnUsed
;
1207 * In case of a processing with compiled macros we need something
1208 * compiled ;-) We let run one instance and let instore() save the compiled
1209 * macros for later use.
1214 thread
[0] = my_threadid();
1215 threadx
[0] = my_threadidx();
1222 UseInstore
= DoInstore
;
1225 if ( stdout_is_tty
)
1228 "You should see a loop counter which stops at %u.\n\n",
1233 * Start some threads and check for errors. Then restart threads up to the
1236 if ( !init_threads() )
1238 fprintf( stderr
, "\n"
1239 "Failure initializing the thread management system.\n\n" );
1243 start
= time( NULL
);
1244 for ( i
= 0; i
< MAX_THREADS
; i
++ )
1246 if ( !start_a_thread( i
) || GlobalError
)
1254 fprintf( stderr
, "\n"
1255 "An error encountered. Do you use the right shared "
1256 "libs or DLLs?\n" );
1261 "Thread tester passed without an error.\n"
1262 "About %u seconds used for %u cyles, each creating a thread.\n",
1263 (unsigned) ( time( NULL
) - start
), total_threads
);
1265 if ( !stdout_is_tty
|| !isatty( fileno( stdout
) ) )
1268 printf( "Press ENTER to continue and end the program. You may have a look\n"
1269 " at your preferred memory analyser like ps, pstat or tasklist...\n" );
1272 fgets( buf
, sizeof( buf
), stdin
);