Subject: FLEX - SCHEDULE ...

From: "Manolis A Vavalis" <[email-masked]>
Date: Thu, 23 Apr 87 16:10:23 EST

Dear  Jack,

	     Here is the static version for SCHEDULE for FLEX/32.
	We will contact the people at FLEX to give us some 
	information on MMOS (operating system of flex),
	so that we can finish the dynamic case.
	     Also soon we will start implementing on the NCUBE.

					Manolis

------------------>>>>>>>>>>cut  here<<<<<<<<<<<<<<<---------------------------
# To unbundle, sh this file
echo README 1>&2
cat > README  <<'End of README'

	Comments on the FLEX implementation of the SCHEDULER

       1) Only the static case has been implemented.
       2) Notice that all the shared variables should be declared shared, 
	  which implies you can't pass them as routine arguments. 
	  (see file stuff.cf)
       3) Since the Flex configuration at Purdue has 7 processors the
          max number of processors is set equal to 6. 
	  (see comment in putq.cc)
       4) Due to local memories we run part of the schedule on all
          processors.
       5) The trace data are written in file 'fort.7'

					Dimitris Alexandrakis 
					Manolis Vavalis
End of README
echo Makefile 1>&2
cat > Makefile  <<'End of Makefile'
#
# 	Makefile for SCHEDULER
#
FILES = stuff.o ftsubs.o putq.o
xtest  :	$(FILES)
	cf77 -f -o xtest $(FILES) 

stuff.o: stuff.cf
	cf77 -f -c stuff.cf
ftsubs.o: ftsubs.cf
	cf77 -f -c ftsubs.cf
putq.o: putq.cc
	ccc -c putq.cc
End of Makefile
echo sh.h 1>&2
cat > sh.h  <<'End of sh.h'
      shared integer /qdata/parmq(14,1000),phead,ptail,readyq(1000),
     *                       rhead, rtail,qtail 

      shared integer /qsync/ipad(1),qlock(1000),barrier,hrlock,
     *                       trlock,qtlock,done,flock,jpad(1000)

End of sh.h
echo ftsubs.cf 1>&2
cat > ftsubs.cf  <<'End of ftsubs.cf'
      subroutine chekin(jobtag)
      integer jobtag
c***********************************************************************
c
c     this subroutine records problem identified by jobtag is 
c     done to appropriate nodes.  these nodes are recorded in 
c     parmq(i,jobtag)  where 5 .le. i .le. nchks+4
c     checkin consists of decrementing the value in each of these
c     locations by 1.  each of these is done in a critical section
c     protected by qlock(ichek)
c
c     if the value in parmq(2,ichek) is 0 where ichek is a process
c     dependent upon this one then ichek is placed on the readyq
c     by entering the critical section protected by rtlock.  the
c     pointer rtail to the tail of the readyq is  incremented
c     unless task done is to be recorded.  task done is placed on
c     the ready q and the pointer rtail left in place if nchks .eq. 0
c     is found. 
c
c     see the common block description in libopn for more detail.
c     
c***********************************************************************
#include 'sh.h'
c
c     common block description:
c
c     a complete common block description is given in the routine libopn
c
c****************************************************************************
c
c     check to see if this process has completed.  if not do not checkin
c
      if (parmq(4,jobtag) .ne. 1) return
c
c     the process has completed so chekin proceeds
c
      call CFlock(ICFret, 1, 'FLOCK')
          if (jobtag .ge. 500) then
	      write(7,15) 5,parmq(5,jobtag),jobtag,0
	  else
	      write(7,15) 2,jobtag,0
  	  endif
  15      format (4i8)
      call CFulck(ICFret, 1, 'FLOCK')
      nchks = parmq(3,jobtag)
c     write(6,*) ' from chekin ... jobtag = ',jobtag,'nchks ',nchks
c
c     if this is the final process (indicated by nchks .eq. 0) then
c     record task done.  do not advance the tail so task done sequence
c     is set.  all subsequent gtprb queries will leave rhead .eq. rtail
c     with readyq(rhead) .eq. done.
c
      if (nchks .eq. 0) then 
            call CFlock(ICFret, 1, 'TRLOCK')
               readyq(rtail) = done
            call CFulck(ICFret, 1, 'TRLOCK')
            return
      endif
      do 50 j = 5,nchks+4
         mychek = parmq(j,jobtag)   
c                  
c     get unique access to the checkin node mychek
c     and checkin by decrementing the appropriate counter
c
         mchkgo = 1
         call CFlock(ICFret, 1, 'QLOCK')
            parmq(2,mychek) = parmq(2,mychek) - 1
            mchkgo = parmq(2,mychek)
         call CFulck(ICFret, 1, 'QLOCK')
c
c     place mychek on readyq if parmq(2,mychek) is 0
c        and if mychek is not an alias
c
c     write(6,*) ' from chekin2 ... jobtag = ',jobtag,'nchks ',nchks,
c    *           'mychek ',mychek, ' mchkgo ',mchkgo
         if (mchkgo .eq. 0 .and. parmq(5,mychek) .ne. -3333 ) then
         call CFlock(ICFret, 1, 'TRLOCK')
               readyq(rtail) = mychek
               rtail = rtail + 1
c              write(6,*) ' from chekin in lock ',mychek
            call CFulck(ICFret, 1, 'TRLOCK')
         endif
   50 continue
c     write(6,*) ' from chekin3 ... jobtag = ',jobtag
      return
c
c     last card of chekin
c
      end
c
c
c
      subroutine _dep(jobtag,icango,nchks,mychkn)
      integer jobtag,icango,nchks,mychkn(*)
c*************************************************************************
c
c  warning - this routine may only be used by driver in a static definition
c            of the data dependencies in the task.
c
c            see the examples stuff.f and stuffspwn.f for usage.
c
c     this subroutine puts data dependencies for problem on the queue.  
c     no synchronization is necessary because each index of a column of 
c     parmq is associated with a jobtag specified by the user and 
c     associated with a unique schedulable process.  the arguments of 
c     putq specify a process and are placed in a column of jobq according
c     to the menue specified in the common block description given below.
c
c     jobtag is an integer specifying a unique schedulable process
c
c
c     icango is a positive integer specifying how many processes must check in
c            to this process before it can be placed on the readyq.
c
c     nchks is the number of processes that depend upon the completion of 
c           this process.
c
c     mychkn is an integer array specifying the jobtags of the processes
c            which depend upon completion of this process.
c
c*************************************************************************
c
#include 'sh.h'
c
c     common block description:
c
c     for a complete common block description see the subroutine libopn
c
c
c     place process jobtag on the problem queue
c
      qtail = qtail + 1
      next = jobtag
      parmq(1,next) = 2
      parmq(2,next) = icango
      parmq(3,next) = nchks
      parmq(4,next) = 1
c
c     specify identifiers of processes which depend on this one
c
      do 50 j = 1,nchks
         parmq(j+4,next) = mychkn(j)
   50 continue
c
      call CFlock(ICFret, 1, 'FLOCK')
	  write(7,60) 0,jobtag, icango, nchks,
     &         (parmq(j,next), j = 5,nchks+4)
  60      format (20i8)
      call CFulck(ICFret, 1, 'FLOCK')
      return
c
c     last card of dep
c
      end
      integer function gtprb(jobtag)
c**************************************************************************
c
c     this routine gets unique access to the head of the readyq
c     claims the pointer to the next schedulable process if there 
c     is one and returns with a nonzero value when there is
c     a process to schedule. if task done has been recorded the value
c     zero is returned in gtprb.  if a nonzero value is returned in 
c     gtprb, the integer jobtag will contain the identifier of the process
c     that is to be scheduled.
c
c     output parameters
c
c       jobtag an integer containing the next process to be executed
c
c       gtprb  the return value of this integer function is:
c       
c               0 if task done has been posted
c
c               nonzero if a schedulable process has been claimed.	
c
c
c***************************************************************************
      integer ireqst,jobtag
c
#include 'sh.h'
c
c
c     common block description:
c
c     for a complete common block description see the routine libopn
c
c
      ireqst = jobtag
   10 continue
      mhead = -1
      call CFlock(ICFret, 1, 'HRLOCK')
c
c     gain access to head of readyq.  if task done has not been recorded
c     then increment the head of the readyq. otherwise the head pointer
c     is left fixed so the next active process will receive task done.
c
c        print*,'rhead,rtail,rtail-rhead ',rhead,rtail,rtail-rhead
         mtail = rtail
         if (rhead .lt. rtail) then
            mhead = rhead
c           print*,'rhead,rtail,rtail-rhead ',mhead,mtail,mtail-mhead
            rhead = rhead + 1
         endif
      call CFulck(ICFret, 1, 'HRLOCK')
      if (mhead .gt. 0) then
c
c        there was a work unit on the readyq
c
c	 print *, 'mhead > 0'
         jobtag = readyq(mhead)
         call CFlock(ICFret, 1, 'FLOCK')
             if (jobtag .ge. 500) then
	         write(7,15) 4,parmq(5,jobtag),jobtag
	     else
	         write(7,15) 1,jobtag
  	     endif
  15         format (3i8)
         call CFulck(ICFret, 1, 'FLOCK')
c     print *, 'after 1st csection from gtprb * JOBTAG, mhead, rtail',
c    $			jobtag ,mhead,rtail
c
         if (jobtag .ne. done) then
c           write(6,*) ' ne done jobtag ', jobtag
c
c           record the subroutine call identifier in gtprb and return
c           the process identifier in jobtag.
c
            gtprb = parmq(1,jobtag)
c
	else
c
c	  task  done has been indicated. request a return from subroutine work
c         by returning the value 0 in gtprb.
c
            gtprb = 0
c           write(6,*) ' eq done jobtag rhead rtail ', jobtag,rhead,rtail
c
         endif
      else 
c
c	 print *, 'mhead < 0'
         jobtag = readyq(rhead)
         if (jobtag .eq. done) then
c
c           task done has been posted
c
c           write(6,*) ' task done posted rhead rtail ',rhead,rtail
            gtprb = 0
            return
c
         else
c
c           there was not any work on the readyq
c           return if call was from a wait condition
c
            gtprb = 1
c	    print *, 'not any work on readyq', ireqst
            if (ireqst .eq. -4444) return
            go to 10
c
         endif
      endif
      return
c
c     last card of gtprb
c
      end
      subroutine libop1(nproc)
      integer nproc
c************************************************************************
c
c     this routine sets locks and initializes variables
c     and then spawns nproc generic work routines.
c
c     nproc is a positive integer.  care should be taken to 
c           match nproc to the number of physical processors 
c           available.
c
c************************************************************************
c
#include 'sh.h'
c
c     common block description:
c
c     common/qdata/
c     
c     parmq is a two dimensional integer array.  each column of 
c           this array represents a schedulable process.  a process is
c           identified by its jobtag which corresponds to a unique 
c           column of parmq.  a column of parmq has the following 
c           entries
c
c                   parmq(1,jobtag) = a nonzero integer unless this is the
c                                     last process
c      
c                   parmq(2,jobtag) = icango 
c                                     an integer specifying the number
c                                     of processes that must check in
c                                     before this process may scheduled
c                                     (ie. be placed on the ready queue)
c
c                   parmq(3,jobtag) = nchks
c                                     an integer specifying the number
c                                     of processes that this process 
c                                     must checkin to.  identifiers of
c                                     these processes are recorded below.
c                                     if nchks .eq. 0 then completion of 
c                                     this process signifies completion of
c                                     task.
c
c                   parmq(4,jobtag) = reentry point
c                                     if this integer is greater than 1
c                                     then the process 
c                                     jobtag has spawned process below it
c                                     and will resume execution at a label
c                                     identified with the value in this location
c                                     (see routines spawn,enter,wait,prtspn)
c
c                   parmq(5:5+nchks,jobtag) is reserved for identifiers of the
c                                                  nchks
c                                     processes that must wait for completion
c                                     of this process before they can execute.
c
c
c     phead pointer to head of parmq
c
c     ptail pointer to tail of parmq
c
c     readyq a one dimensional integer array that holds the jobtags of those
c            processes that are ready to execute.
c            if readyq(j) .eq. done has been set then a return from subroutine
c            work is indicated.
c
c     rhead is a pointer to the head of readyq
c
c     rtail is a pointer to the tail of readyq
c
c     common/sync/
c
c     qlock is an integer array of locks.  there is one lock for each 
c           column of parmq.  the purpose of this lock is to ensure
c           unique access to a column of parmq during the checkin operation.
c
c     hrlock is an integer lock.  the purpose of this lock is to ensure 
c            unique access to the pointer rhead to the head of the readyq.
c
c     trlock is an integer lock.  the purpose of this lock is to ensure 
c            unique access to the pointer rtail to the tail of the readyq.
c
c     done   is a unique non positive integer set in libopn to indicate
c            task done.
c
      integer FD
c
      done = 0
c
c     set qlocks off
c
      do 100 j = 1,1000
         readyq(j) = -1
         parmq(4,j) = 1
  100 continue
c
c     set readyq locks off
c
c
c     initialize queue pointers
c
      phead = 1
      ptail = 1
      rhead = 1
      rtail = 1
      qtail = 2
      next = 1 
c
c     set lock on pointer to head of readyq so 
c     no process may start until all process and data dependencies
c     have been specified by the user supplied routine driver.
c
c     call CFlock(ICFret, 1, 'HRLOCK')
c
      return
c
c     last card of libop1
c
      end
      subroutine libop2(nproc)
      integer nproc

      shared integer /lib2/ itag(20),ispace(20),jspace(20)           

      integer procid(20), compno
      external work
c
c     now spawn virtual processors.  these generic work routines will
c     assume the identity of any schedulable process specified by driver.
c
      do 150 j = 1,nproc
         itag(j) = j
  150 continue
c
c     write(6,*) ' in libopn2 , before COEND -->  nprocs = ' ,nproc

      COEND
        do 200 j = 1,nproc
	      
	  compno = j+1
          PROCESS(procid(j),work(itag(j),ispace(j),jspace(j)), compno)

c         call m_fork(_work,itag(j),ispace(j),jspace(j))
c         call work(itag(j),ispace(j),jspace(j))
  200   continue
      END COEND
      return
c
c     last card of libopn
c
      end
      subroutine nxtag(mypar,jobtag)
      integer mypar,jobtag
c*************************************************************************
c
c  warning - this routine may only be used by driver in a static definition
c            of the data dependencies in the task.
c
c     this subroutine puts data dependencies for problem on the queue.  
c     no synchronization
c     is necessary because each index of a column of parmq is associated
c     with a jobtag specified by the user and associated with a unique
c     schedulable process.  the arguments of putq specify a process and are
c     placed in a column of jobq according to the menue specified in the
c     common block description given below.
c
c     jobtag is an integer specifying a unique schedulable process
c
c
c     icango is a positive integer specifying how many processes must check in
c            to this process before it can be placed on the readyq.
c
c     nchks is the number of processes that depend upon the completion of 
c           this process.
c
c     mchkin is an integer array specifying the jobtags of the processes
c            which depend upon completion of this process.
c
c*************************************************************************
c
#include 'sh.h'
c
c
c     common block description:
c
c     for a complete common block description see the subroutine libopn
c
c
c
c     place this process on the next slot in the problem queue
c
      call CFlock(ICFret, 1, 'QTLOCK')
         next = qtail
         qtail = qtail + 1
      call CFulck(ICFret, 1, 'QTLOCK')
      call CFlock(ICFret, 1, 'FLOCK')
	  write(7,15) 3,mypar,next
  15      format (3i8)
      call CFulck(ICFret, 1, 'FLOCK')
      jobtag = next
      parmq(1,next) = 2
      parmq(2,next) = 0
      parmq(3,next) = 1     
      parmq(5,next) = mypar
      parmq(2,mypar) = parmq(2,mypar) + 2
      parmq(6,mypar) = parmq(6,mypar) + 1
c
c        indicate that this is not a reenter 
c
         parmq(4,next) = 1
c
c
c
      return
c
c     last card of nxtag
c
      end
      subroutine start2
c
c     this routine allows parallel processing to start after user supplied
c     driver has completed by unlocking the head of the readyq
c
#include 'sh.h'
c
c
c     for common block description see subroutine libopn.
c
c	print *, ' in start2  -- about to unlock hrlock '
c     call CFulck(ICFret, 1, 'HRLOCK')
c
      return
c
c     last card of start2
c
      end
      subroutine wait(jobtag)
c
      integer jobtag
c*****************************************************************************
c
c     this routine will allow process jobtag to continue after
c     spawned processes have all checked in.  it must be called if 
c     processes have been spawned by an executing process through 
c     the use of the subroutines prtspn and spawn.  
c     the required syntax is 
c
c          call prtspn(mytag)
c          do 100 j = 1,nproc
c                 .
c                 . (set parameters to define spawned process)
c                 .
c             call spawn(mytag,subname,<parms>)
c      100 continue
c          call wait(mytag)
c
c     there is an implied barrier at the call to wait.  the statement
c     following the call to wait is not executed until all spawned 
c     processes have completed execution.
c
c*****************************************************************************
c
#include 'sh.h'
      integer nprocs
c
c        
c     decrement the icango counter so this process can be rescheduled
c     as soon as all spawned processes have checked in.
c
      nprocs = parmq(6,jobtag)
  100 continue
      if (parmq(2,jobtag) .eq. nprocs) then
         return
      else
         jdummy = -4444
         call work2(jdummy,idummy)
      endif
      go to 100
c
c     last card of wait
c
      end
      subroutine place(jobtag)
      integer jobtag
c*************************************************************************
c
c
c      this subroutine places a problem on the readyq 
c
c     jobtag is an integer specifying a unique schedulable process
c
c
c     icango is a positive integer specifying how many processes must check in
c            to this process before it can be placed on the readyq.
c
c     nchks is the number of processes that depend upon the completion of 
c           this process.
c
c     mchkin is an integer array specifying the jobtags of the processes
c            which depend upon completion of this process.
c
c*************************************************************************
c
c
#include 'sh.h'
c
c
c     common block description:
c
c     for a complete common block description see the subroutine libopn
c
c
c
c     place this process on readyq if icango is 0
c     when icango .eq. 0 this process does not depend on any
c     others.
c
      icango = parmq(2,jobtag)
c	print *, 'in place - jobtag,icango ', jobtag,icango
      if (icango .eq. 0 ) then
         call CFlock(ICFret, 1, 'TRLOCK')
            readyq(rtail) = jobtag
c	print *, 'in place rtail,readyq(rtail)',rtail,readyq(rtail)
            rtail = rtail + 1
         call CFulck(ICFret, 1, 'TRLOCK')
      endif
      return
      end
      subroutine prtspn(jobtag)
      integer jobtag,nprocs
c*************************************************************************
c
c
c     this subroutine reserves a slot in the problem queue pointed to by 
c     jobtag.  jobtag will be returned to calling routine through
c     the calling sequence.   jobtag will be an alias for the parent 
c     process that has called this routine. the syntax for the calling
c     program should be 
c
c
c          call prtspn(mytag)
c          do 100 j = 1,nproc
c                 .
c                 . (set parameters to define spawned process)
c                 .
c             call spawn(mytag,subname,<parms>)
c      100 continue
c          call wait(mytag)
c
c
c
c*************************************************************************
c
#include 'sh.h'
c
c
c     common block description:
c
c     for a complete common block description see the subroutine libopn
c
c
c
c     place this process on the next slot in the problem queue
c
      call CFlock(ICFret, 1, 'QTLOCK')
         next = qtail
         qtail = qtail + 1
      call CFulck(ICFret, 1, 'QTLOCK')
      jobtag = next
      parmq(1,next) = 2
      parmq(2,next) = 0
      parmq(3,next) = 1     
      parmq(5,next) = -3333
      parmq(6,next) = 0
c
c     the number -3333 indicates that this is an alias for a parent
c     process.
c
c        indicate that this is not a reenter 
c
         parmq(4,next) = 1
c
c
c
      return
c
c     last card of prtspn
c
      end
End of ftsubs.cf
echo putq.cc 1>&2
cat > putq.cc  <<'End of putq.cc'
#include <stdio.h>

#define MAXPARMS	20

       /* structure to save addresses of subroutines for each processor */
struct lparms {
	int	(*subname)();
};
struct lparms lparms[1000];

struct parms	{
	int	(*subname)();
	long	*parms[MAXPARMS];
		};
struct parms indx[1000]; 

int procid[20];

sched_(nprocs,parms)
int *nprocs;
struct parms parms;
/*  
    this procedure obtains nproc physical processors devoted
    to the the execution of the parallel program indicated through parms
    which is a structure whos first entry is a subroutine name and whow
    remaining entries are parameters appearing in the calling sequence
    of that subroutine.
*/
{
        int libopn_(), (*subname)();
	int j, compno;

	bcopy(&parms, &indx[0], sizeof(struct parms));
	bcopy(&indx[0].subname, &subname, sizeof(subname));
/*      

           the subroutine name and prameter list have been copied and 
           placed in a special slot on the parmq      
          
           then libopn is invoked to initialize pointers grab physical
           processors and begin the computation
*/

/*
    Due to local configuration max number of processors has been set 
    equal to 6
*/
      if (*nprocs >= 6) *nprocs = 6;	

      libop1_(nprocs);     /*  do initialization on data structures  */
      coend { 
	     /*  call the driver for each processor in order to get
		 addresses of the subroutines needed by the scheduler */
	for (j = 1; j <= *nprocs; j++) {
	  compno = j+1;
          process(procid[j], 
	       subname(indx[0].parms[0], indx[0].parms[1],
			       indx[0].parms[2], indx[0].parms[3],
			       indx[0].parms[4], indx[0].parms[5],
			       indx[0].parms[6], indx[0].parms[7],
			       indx[0].parms[8], indx[0].parms[9],
			       indx[0].parms[10], indx[0].parms[11],
			       indx[0].parms[12], indx[0].parms[13],
			       indx[0].parms[14], indx[0].parms[15],
			       indx[0].parms[16], indx[0].parms[17],
			       indx[0].parms[18], indx[0].parms[19]),
		compno);
	}
      }
        libop2_(nprocs);  
	return(0);
}
/*
 *   dep_()  --  call dep() only once to initialize data structures
 */
dep_(jobtag,icango,nchks,mychkn)
int *jobtag,*icango,*nchks,mychkn[];
{
	int ptag;

        if ( (ptag = CCptag()) == procid[1] )  /* only #1 will call dep() */
		_dep_(jobtag,icango,nchks,mychkn);
	return(0);
}
putq_(jobtag,parms)
int *jobtag;
struct parms	parms;
/*  
    this procedure puts the descriptor of a schedulable process <jobtag>
    onto the problem queue.  this process will be scheduled for execution
    when its data dependencies have been satisfied (indicated by icango==0).
    the argument parms is a structure whos first entry is a subroutine name 
    and whos remaining entries are parameters appearing in the calling sequence
    of that subroutine.
*/
{
        register int j;
	int ptag, place_();

        j = *jobtag;
	bcopy(&parms, &indx[j], sizeof(struct parms));
 	   /* copy address of subroutine in local memory of processor */
	bcopy(&indx[j].subname, &lparms[j], sizeof(struct lparms));
/*
        first the parms block is copied into the slot pointed to by 
        by jobtag and then this descriptor is placed on the problem 
        queue
*/ 
	if ( (ptag = CCptag()) == procid[1] ) /* only #1 calls place_() */
		place_(jobtag);
	return(0);
}
spawn_(parent,jobtag,parms)    /*  NOT IMPLEMENTED */
int *parent,*jobtag;
struct parms	parms;
/*  
    this procedure puts the descriptor of a schedulable process <jobtag>
    onto the problem queue.  this process will be scheduled for execution
    when its data dependencies have been satisfied (indicated by icango==0).
    the argument parms is a structure whos first entry is a subroutine name 
    and whos remaining entries are parameters appearing in the calling sequence
    of that subroutine.
    
    the action of this procedure differs from putq in that the user does not
    assign jobtags or data dependencies.  a parent may spawn any number of 
    children but these child processes only report to the parent.
*/
{
        register int j,i;
        int nxtag_(), place_(),clone_();
        nxtag_(parent,jobtag);
        j = *jobtag;
        i = *parent;
	bcopy(&parms, &indx[j], sizeof(struct parms));
/*
        first the parms block is copied into the slot pointed to by 
        by jobtag and then this descriptor is placed on the problem 
        queue
*/ 
        if (indx[j].subname == clone_) indx[j].subname = indx[i].subname;
/*
        here we ask if this is a recursive spawning.  if so the name
        clone has been called instead of subname so we replace the name
        clone by subname.
*/
        place_(jobtag);
	return(0);
}
clone_()
{
/*
        this is a dummy routine to satisfy unresolved external
*/
        return(0);
}
work_(id,jobtag,myjob)
int *id,*jobtag,*myjob;
{
        int start2_(),gtprb_();
        register int j;

        j = *id;
        *myjob = gtprb_(jobtag);
        while (*myjob != 0) 
        {
           j = *jobtag;
	   lparms[j].subname(indx[j].parms[0], indx[j].parms[1],
			       indx[j].parms[2], indx[j].parms[3],
			       indx[j].parms[4], indx[j].parms[5],
			       indx[j].parms[6], indx[j].parms[7],
			       indx[j].parms[8], indx[j].parms[9],
			       indx[j].parms[10], indx[j].parms[11],
			       indx[j].parms[12], indx[j].parms[13],
			       indx[j].parms[14], indx[j].parms[15],
			       indx[j].parms[16], indx[j].parms[17],
			       indx[j].parms[18], indx[j].parms[19]);
             chekin_(jobtag); 
             *myjob = gtprb_(jobtag);
        }
        return(0);
}
work2_(jobtag,myjob)
int *jobtag,*myjob;
{
        int gtprb_();
        register int j;
         *myjob = gtprb_(jobtag);
         if (*myjob == 2) 
         {
           j = *jobtag;
	   indx[j].subname(indx[j].parms[0], indx[j].parms[1],
			       indx[j].parms[2], indx[j].parms[3],
			       indx[j].parms[4], indx[j].parms[5],
			       indx[j].parms[6], indx[j].parms[7],
			       indx[j].parms[8], indx[j].parms[9],
			       indx[j].parms[10], indx[j].parms[11],
			       indx[j].parms[12], indx[j].parms[13],
			       indx[j].parms[14], indx[j].parms[15],
			       indx[j].parms[16], indx[j].parms[17],
			       indx[j].parms[18], indx[j].parms[19]);
             chekin_(jobtag); 
          }
          return(0);
}
/* 
 *	bcopy()  --  block copy from from to to, count bytes 
 */
bcopy(from, to, count)
	char *from, *to;
	int count;
{
	while ((count--) > 0)	/* mjm */
		*to++ = *from++;
}
End of putq.cc
echo stuff.cf 1>&2
cat > stuff.cf  <<'End of stuff.cf'
c
c	parallel main   --   Test for Scheduler
c
c	find the maximum element of an array of 2^k elements
c
	program main
c
c	declare shared variables
c
	shared integer /ints/ n,k

	shared real /reals/ a(1000)
 
	EXTERNAL DRIVER
c	
	print *, 'input k, NPROCS'
	read *, k, NPROCS
	n = 2**k
	do 10 i = 1, n
 10	a(i) = i
c
 	CALL SCHED(nprocs, DRIVER)
c
	print *, 'MAIN  >>>>> max = ', a(1)
	stop
	end
c
c
c
 	subroutine driver
c
c	declare shared variables
c
	shared integer /ints/ n,k

	shared real /reals/ a(1000)
 
c
c	declare local variables
c
	integer mychkn(10), icango, ncheks, jobtag
	EXTERNAL MAXI
c
	job = 0
	do 100 j = 1, k
	    istep = 2**j
	    istop = n+1-istep
	    idstep = n / (2**j)

	    do 200 i = 1, istop, istep
		job = job + 1
		if (mod(job,2) .eq. 0) idstep = idstep-1
		inext = i + 2**(j-1) 
 		JOBTAG = job
		n2 = n/2
		if (job .le. n2) then
 			ICANGO = 0
		else
 			ICANGO = 2
		endif
		if (j .eq. k) then
		    NCHEKS = 0
		else
 		    NCHEKS = 1
		    MYCHKN(1) = job + idstep 
		endif
c 
 		CALL DEP(jobtag,icango,ncheks,mychkn)
 		CALL PUTQ(jobtag, MAXI,a(i),a(inext),a(i))
c
c		call maxi(a(i),a(inext),a(i))
c
  200	    continue
  100	continue
	return
	end
c
c	find maximum of x & y -- return in xymax
c
	subroutine maxi(x,y,xymax)	
	real x, y, xymax
c
	print *, 'in max(a,b,max)  >>  a,b is: ',x,y
	if (x .le. y) then
		xymax = y
	else
		xymax = x
	endif
	print *, ' *** max  = ',xymax
	return
	end
End of stuff.cf
echo trace.16 1>&2
cat > trace.16  <<'End of trace.16'
       0       1       0       1       9
       0       2       0       1       9
       0       3       0       1      10
       0       4       0       1      10
       0       5       0       1      11
       0       6       0       1      11
       0       7       0       1      12
       0       8       0       1      12
       0       9       2       1      13
       0      10       2       1      13
       0      11       2       1      14
       0      12       2       1      14
       0      13       2       1      15
       0      14       2       1      15
       0      15       2       0
       1       1
       1       6
       1       2
       1       3
       1       4
       1       5
       2       1       0
       2       6       0
       2       2       0
       2       3       0
       2       4       0
       2       5       0
       1       7
       1       8
       1       9
       1      10
       1      11
       2       7       0
       2       8       0
       2       9       0
       2      10       0
       2      11       0
       1      12
       1      13
       2      12       0
       1      14
       2      13       0
       2      14       0
       1      15
       2      15       0
End of trace.16
echo trace.32 1>&2
cat > trace.32  <<'End of trace.32'
       0       1       0       1      17
       0       2       0       1      17
       0       3       0       1      18
       0       4       0       1      18
       0       5       0       1      19
       0       6       0       1      19
       0       7       0       1      20
       0       8       0       1      20
       0       9       0       1      21
       0      10       0       1      21
       0      11       0       1      22
       0      12       0       1      22
       0      13       0       1      23
       0      14       0       1      23
       0      15       0       1      24
       0      16       0       1      24
       0      17       2       1      25
       0      18       2       1      25
       0      19       2       1      26
       0      20       2       1      26
       0      21       2       1      27
       0      22       2       1      27
       0      23       2       1      28
       0      24       2       1      28
       0      25       2       1      29
       0      26       2       1      29
       0      27       2       1      30
       0      28       2       1      30
       0      29       2       1      31
       0      30       2       1      31
       0      31       2       0
       1       1
       1       6
       1       2
       1       3
       1       4
       1       5
       2       1       0
       2       6       0
       2       2       0
       2       3       0
       2       4       0
       2       5       0
       1       7
       1       8
       1       9
       1      10
       1      11
       1      12
       2       7       0
       2       8       0
       2       9       0
       2      10       0
       2      11       0
       2      12       0
       1      13
       1      14
       1      15
       1      16
       1      17
       1      18
       2      13       0
       2      14       0
       2      15       0
       2      16       0
       2      17       0
       2      18       0
       1      19
       1      20
       1      21
       1      22
       1      23
       1      24
       2      19       0
       2      20       0
       2      21       0
       2      22       0
       2      23       0
       2      24       0
       1      25
       1      26
       1      27
       1      28
       2      25       0
       2      26       0
       2      27       0
       2      28       0
       1      29
       1      30
       2      29       0
       2      30       0
       1      31
       2      31       0
End of trace.32
echo done ... 1>&2