forgotten commit. disabled until egl is adapted.
[AROS-Contrib.git] / regina / threader.c
blob5c55445c3c4fb83309e1fe2561ceda1bc9fbcdf2
1 /*
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.
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <limits.h>
29 #include <errno.h>
30 #include <time.h>
32 #ifdef POSIX_THREADS
33 # include <sys/time.h>
34 # include <unistd.h>
35 # include <pthread.h>
36 # define USE_SEMAPHORES 0
37 # ifdef _POSIX_SEMAPHORES
38 # include <semaphore.h>
39 # include <signal.h>
40 # undef USE_SEMAPHORES
41 # define USE_SEMAPHORES 1
42 # endif
43 #endif
45 #ifdef OS2_THREADS
46 # include <io.h>
47 # include <stddef.h>
48 # include <process.h>
49 # define INCL_DOSSEMAPHORES
50 # define INCL_ERRORS
51 # include <os2.h>
52 # define CHAR_TYPEDEFED
53 # define SHORT_TYPEDEFED
54 # define LONG_TYPEDEFED
55 # ifndef _OS2EMX_H
56 # define _OS2EMX_H /* prevents PFN from defining (Watcom) */
57 # endif
58 #endif
60 #ifdef _MSC_VER
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)
67 #endif
69 #ifdef WIN32_THREADS
70 # define WIN32_LEAN_AND_MEAN
71 # include <process.h>
72 # include <windows.h>
73 # include <io.h>
74 #endif
76 #ifdef _MSC_VER
77 # pragma warning(default:4100 4115 4201 4214)
78 #endif
80 #define INCL_RXSHV
81 #define INCL_RXFUNC
82 #define INCL_RXSYSEXIT
83 #define INCL_RXSUBCOM
85 #ifdef USE_OREXX
86 # include "rexx.h"
87 #else
88 # include "rexxsaa.h"
89 #endif
92 * MAX_THREADS is the number of parallel starting threads. 20 is a good
93 * maximum.
95 #define MAX_THREADS 20
98 * MAX_RUN defines the number of loops each thread has to perform.
99 * Don't modify.
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;
117 #ifdef POSIX_THREADS
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];
127 #endif
129 #ifdef OS2_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];
140 #endif
142 #ifdef WIN32_THREADS
144 * ThreadIndexType is the type of my_threadidx which shall be the thread
145 * identifier.
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];
175 #endif
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".
187 static enum {
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
207 * to the output.
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.
215 static enum {
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 */
244 RXSTRING rx;
245 unsigned len,tid,loop;
246 int rc,idx;
248 if ( ( ExNum != RXSIO ) || ( Subfun != RXSIOSAY ) ) /* unexpected? */
249 return RXEXIT_NOT_HANDLED;
251 if ( GlobalError )
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() );
264 GlobalError = 1;
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 );
274 if ( rc != 2 )
276 fprintf( stderr, "\n"
277 "Thread %lu gives an unexpected SAY output \"%s\".\n",
278 ( unsigned long ) my_threadidx(), buf );
279 GlobalError = 1;
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 );
292 GlobalError = 1;
293 return RXEXIT_HANDLED;
297 * Is this a known thread?
299 for ( idx = 0; idx < MAX_THREADS; idx++ )
301 if ( threadx[idx] == ( ThreadIndexType ) tid )
302 break;
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() );
310 GlobalError = 1;
311 return RXEXIT_HANDLED;
315 * Check the loop number. We may have lost or duplicated data.
317 rc = 0; /* OK */
318 if ( ( loop < 1 ) || ( loop > MAX_RUN ) )
319 rc = 1;
320 else
322 loop = 1 << ( loop - 1 ); /* Bitmask for the loop */
323 if ( found[idx] & loop )
324 rc = 1; /* already found */
325 found[idx] |= loop;
328 if ( rc )
330 fprintf( stderr, "\n"
331 "Thread %lu's loop doesn't run continuously.\n",
332 ( unsigned long ) my_threadidx() );
333 GlobalError = 1;
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 )
347 RXSTRING Instore[2];
348 RXSYSEXIT Exits[2];
349 char instore_buf[256];
350 int rc;
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 */
361 #ifdef RX_WEAKTYPING
362 (PFN) instore_exit, /* entry point of the handler */
363 #else
364 instore_exit, /* entry point of the handler */
365 #endif
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();"
377 "End;"
378 "Return 0",
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,
387 * it is ignored.
389 Instore[1].strptr = (char *)InstoreBuf;
390 Instore[1].strlength = InstoreLen;
392 else
394 Instore[1].strptr = NULL;
396 rc = RexxStart( 0, /* ArgCount */
397 NULL, /* ArgList */
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 )
407 case UnUsed:
409 * We will get an instore macro even if not desired. Delete it.
411 if ( Instore[1].strptr )
412 RexxFreeMemory( Instore[1].strptr );
413 else
415 GlobalError = 1;
416 fprintf( stderr, "\n"
417 "Didn't got the instore macro.\n" );
419 break;
421 case FirstRun:
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;
430 else
432 GlobalError = 1;
433 fprintf( stderr, "\n"
434 "Didn't got the instore macro.\n" );
436 break;
438 default:
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().
444 break;
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 );
453 #ifdef REGINAVERSION
454 ReginaCleanup();
455 #endif
456 #ifndef THREAD_RETURN_VOID
457 return ( THREAD_RETURN ) 0;
458 #endif
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 )
470 int rc;
473 * signal that we are alive.
475 threadx[(unsigned) data] = my_threadidx();
477 rc = RexxStart( 0, /* ArgCount */
478 NULL, /* ArgList */
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 );
491 #ifdef REGINAVERSION
492 ReginaCleanup();
493 #endif
494 #ifndef THREAD_RETURN_VOID
495 return ( THREAD_RETURN ) 0;
496 #endif
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))
516 fprintf(stderr,"\n"
517 "Thread %lu has stopped without completing its loop.\n",
518 (unsigned long) threadx[position]);
519 GlobalError = 1;
521 found[position] = 0;
524 #ifdef WIN32_THREADS
526 * init_threads initializes the usage of our thread management system.
527 * Returns 1 on success, 0 otherwise.
529 int init_threads( void )
531 return 1;
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 )
541 int rc;
542 unsigned threadID;
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
550 * performance.
552 thread[position] = ( HANDLE ) _beginthreadex( NULL,
554 (program_name) ? external : instore,
555 ( void * ) position,
556 CREATE_SUSPENDED,
557 &threadID );
558 rc = ( long ) thread[position] != 0l;
559 if ( !rc )
561 fprintf( stderr, "\n"
562 "Error starting thread, error code is %ld\n",
563 GetLastError() );
564 GlobalError = 1;
565 ThreadHasStopped( position );
567 ResumeThread( thread[position] );
568 return rc;
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;
589 DWORD rc;
590 HANDLE compressed[MAX_THREADS];
591 unsigned BackSort[MAX_THREADS];
593 running = done = MAX_THREADS;
595 if ( stdout_is_tty )
596 printf( "%u\r", MAX_THREADS );
597 if ( GlobalError )
598 return;
600 for ( ; ; )
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];
612 BackSort[j] = i;
613 j++;
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() );
633 GlobalError = 1;
635 if ( GlobalError )
636 break;
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);
646 GlobalError = 1;
647 break;
651 * Destroy associated buffers, check values and
652 * restart a new instance if we still have to do so.
654 CloseHandle( thread[i] );
655 State[i] = Ready;
656 running--;
658 * Only reap our threads if running the instore test code
660 if ( program_name == NULL )
661 reap( i );
663 if ( done < total_threads )
665 if ( !start_a_thread( i ) )
666 break;
667 done++;
668 running++;
670 if ( stdout_is_tty )
671 printf( "%u(%u)\r", done, running );
672 if ( GlobalError || !running )
673 break;
676 #endif
678 #ifdef OS2_THREADS
680 * init_threads initializes the usage of our thread management system.
681 * Returns 1 on success, 0 otherwise.
683 HMUX hmux;
684 SEMRECORD thread_sems[MAX_THREADS];
686 int init_threads( void )
688 int i;
689 LONG rc;
691 for ( i = 0; i < MAX_THREADS; i++ )
693 thread_sems[i].ulUser = i;
694 rc = DosCreateEventSem( NULL,
695 (PHEV) &thread_sems[i].hsemCur,
697 0 );
698 if ( rc != 0 )
700 fprintf( stderr, "\n"
701 "Error creating an EventSem, error code is %lu\n",
702 rc );
703 return 0;
707 rc = DosCreateMuxWaitSem( NULL,
708 &hmux,
709 MAX_THREADS,
710 thread_sems,
711 DCMW_WAIT_ANY);
712 if ( rc != 0 )
714 fprintf( stderr, "\n"
715 "Error creating a MuxWaitSem, error code is %lu\n",
716 rc );
717 return 0;
719 return 1;
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 )
729 ULONG rc, post;
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",
736 rc );
737 GlobalError = 1;
738 return 0;
740 State[position] = Running;
741 thread[position] = _beginthread( (program_name) ? external : instore,
742 NULL,
743 0x8000,
744 ( void * ) position );
745 if ( thread[position] == -1 )
747 fprintf( stderr, "\n"
748 "Error starting thread, error code is %d\n",
749 errno );
750 GlobalError = 1;
751 State[position] = Stopped;
752 DosPostEventSem( (HEV) thread_sems[position].hsemCur );
753 return 0;
755 return 1;
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 )
764 ULONG rc;
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",
771 rc );
772 GlobalError = 1;
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;
789 if ( stdout_is_tty )
790 printf( "%u\r", MAX_THREADS );
791 if ( GlobalError )
792 return;
794 for ( ; ; )
796 rc = DosWaitMuxWaitSem( hmux, timeout_seconds*1000, &user );
797 if ( rc != 0 )
799 fprintf( stderr, "\n"
800 "At least one thread won't finish normally within 3 seconds (error=%lu).\n",
801 rc );
802 GlobalError = 1;
804 if ( user >= MAX_THREADS )
806 fprintf( stderr, "\n"
807 "Strange behaviour after wating for MuxWaitSem, released thread index is %lu.\n",
808 user );
809 GlobalError = 1;
811 if ( GlobalError )
812 break;
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 );
821 GlobalError = 1;
822 break;
826 * Check values and restart a new instance if we still have to do so.
828 State[user] = Ready;
829 running--;
831 * Only reap our threads if running the instore test code
833 if ( program_name == NULL )
834 reap( (int) user );
836 if ( done < total_threads )
838 if ( !start_a_thread( (int) user ) )
839 break;
840 done++;
841 running++;
843 else
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",
850 rc );
851 GlobalError = 1;
852 break;
855 if ( stdout_is_tty )
856 printf( "%u(%u)\r", done, running );
857 if ( GlobalError || !running )
858 break;
861 #endif
863 #ifdef POSIX_THREADS
865 * The number of processed runs needs to be global for error analysis.
867 static unsigned done = 0;
869 #if USE_SEMAPHORES
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 ) )
882 lastvalue = done;
883 signal( SIGALRM, timer_alarm );
884 return;
887 GlobalError = 1;
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 )
897 exit( 1 );
899 #else
900 static pthread_mutex_t thread_lock = PTHREAD_MUTEX_INITIALIZER;
901 static pthread_cond_t something_stopped = PTHREAD_COND_INITIALIZER;
902 #endif
905 * init_threads initializes the usage of our thread management system.
906 * Returns 1 on success, 0 otherwise.
908 int init_threads( void )
910 #if USE_SEMAPHORES
911 if ( sem_init( &something_stopped, 0, 0 ) != 0 )
912 return 0;
913 #endif
914 return 1;
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 )
924 int rc;
926 State[position] = Running;
927 rc = pthread_create( &thread[position], NULL, (program_name) ? external : instore, (void *) position );
928 if ( rc )
930 fprintf( stderr, "\n"
931 "Error starting thread, error code is %d\n",
932 rc );
933 GlobalError = 1;
934 ThreadHasStopped( position );
936 return(!rc);
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
944 * death.
946 static void ThreadHasStopped( unsigned position )
948 #if USE_SEMAPHORES
949 State[position] = Stopped;
950 sem_post( &something_stopped );
951 #else
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 );
959 #endif
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 )
974 unsigned i,running;
975 int rc;
976 #if !USE_SEMAPHORES
977 struct timeval now;
978 struct timespec timeout;
979 #else
980 struct itimerval ival;
981 #endif
983 running = done = MAX_THREADS;
984 if ( stdout_is_tty )
985 printf( "%u\r", MAX_THREADS );
987 if ( GlobalError )
988 return;
990 #if !USE_SEMAPHORES
992 * The lock is needed by pthread_cond_timewait.
993 * The multithreading paradigma here is very unefficient.
995 pthread_mutex_lock( &thread_lock );
996 #else
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 );
1002 #endif
1004 for ( ; ; )
1006 #if !USE_SEMAPHORES
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" );
1023 GlobalError = 1;
1025 #else
1026 if ( sem_wait( &something_stopped ) != 0 )
1028 fprintf( stderr, "\n"
1029 "Interrupted wait.\n" );
1030 GlobalError = 1;
1032 #endif
1033 if ( GlobalError )
1034 break;
1037 * Restart the threads is appropriate.
1039 for ( i = 0; i < MAX_THREADS; i++ )
1041 if ( State[i] != Stopped )
1042 continue;
1043 State[i] = Ready;
1044 running--;
1045 rc = pthread_join( thread[i], NULL );
1046 if ( rc != 0 )
1048 fprintf( stderr, "\n"
1049 "A thread can't be found in the internal table.\n" );
1050 GlobalError = 1;
1051 break;
1055 * Has the thread done its work completely?
1058 * Only reap our threads if running the instore test code
1060 if ( program_name == NULL )
1061 reap(i);
1064 * Restart a new thread if we need some more runs.
1066 if ( (int) done < total_threads )
1068 if ( !start_a_thread( i ) )
1069 break;
1070 done++;
1071 running++;
1074 if ( stdout_is_tty )
1075 printf( "%u(%u)\r", done, running );
1076 if ( GlobalError || !running )
1077 break;
1079 #if !USE_SEMAPHORES
1080 pthread_mutex_unlock( &thread_lock );
1081 #endif
1083 #endif
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"
1091 "\n"
1092 "Options:\n"
1093 "-p\tLoad the macro only once and then use the generated instore\n"
1094 "\tmacro. Default: Always load the macro new.\n"
1095 "\n"
1096 "-q\tRun quietly. Don't display running progress information.\n"
1097 "\n"
1098 "-t\ttotal_threads\tTotal number of threads to execute.\n"
1099 "\n"
1100 "-s\ttimeoutseconds\tNumber of seconds to timeout a thread. Default 3.\n"
1101 "\n"
1102 "filename\tThe Rexx program to execute rather than the instore test\n"
1103 "\tprogram.\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"
1109 "\n"
1110 "This program is for testing Regina's multithreading capabilities.\n"
1111 ,total_threads );
1112 exit( 1 );
1115 int main( int argc, char *argv[] )
1117 RXSTRING version;
1118 ULONG versioncode;
1119 int i;
1120 time_t start;
1123 * In case of a connected tty we let run a counter to show the user some
1124 * "success".
1126 if ( isatty( fileno( stdout ) ) )
1127 stdout_is_tty = 1;
1129 for ( i = 1; i < argc; i++ )
1131 if ( argv[i][0] != '-' )
1132 break;
1134 if ( strcmp( argv[i], "-p" ) == 0 )
1136 UseInstore = FirstRun;
1138 else if ( strcmp( argv[i], "-t" ) == 0 )
1140 i++;
1141 total_threads = atoi( argv[i] );
1143 else if ( strcmp( argv[i], "-s" ) == 0 )
1145 i++;
1146 timeout_seconds = atoi( argv[i] );
1148 else if ( strcmp( argv[i], "-q" ) == 0 )
1150 stdout_is_tty = 0;
1152 else if ( strcmp( argv[i], "--" ) == 0 )
1154 i++;
1155 break;
1157 else
1159 usage();
1163 if ( argc > i )
1165 program_name = argv[i];
1166 i++;
1168 if ( argc > i )
1169 usage();
1172 * Initialize some tables and tune the IO system to show every output
1173 * at once.
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",
1191 versioncode >> 8,
1192 versioncode & 0xFF );
1193 if ( version.strptr )
1195 printf( " (in complete \"%s\")", version.strptr );
1196 RexxFreeMemory( version.strptr );
1198 #endif
1199 printf( "\n" );
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.
1211 if ( UseInstore )
1213 State[0] = Running;
1214 thread[0] = my_threadid();
1215 threadx[0] = my_threadidx();
1216 if ( program_name )
1217 external( NULL );
1218 else
1219 instore( NULL );
1220 State[0] = Stopped;
1221 found[0] = 0;
1222 UseInstore = DoInstore;
1225 if ( stdout_is_tty )
1227 printf( "\n"
1228 "You should see a loop counter which stops at %u.\n\n",
1229 total_threads );
1233 * Start some threads and check for errors. Then restart threads up to the
1234 * maximum count.
1236 if ( !init_threads() )
1238 fprintf( stderr, "\n"
1239 "Failure initializing the thread management system.\n\n" );
1240 return 1;
1243 start = time( NULL );
1244 for ( i = 0; i < MAX_THREADS; i++ )
1246 if ( !start_a_thread( i ) || GlobalError )
1247 break;
1249 if ( !GlobalError )
1250 wait_for_threads();
1252 if ( GlobalError )
1254 fprintf( stderr, "\n"
1255 "An error encountered. Do you use the right shared "
1256 "libs or DLLs?\n" );
1257 return 1;
1260 printf( "\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 ) ) )
1266 return 0;
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" );
1271 char buf[128];
1272 fgets( buf, sizeof( buf ), stdin );
1275 return 0;