#if 0
+-----------------------------------------------------------------------------+
|  Program:   doall                            Jace A Mogill                  |
|  Release:   2.0                              ROTANG UnderWare               |
|  Date:      $Date: 2004/01/24 03:43:09 $     http://www.rotang.com/         |
|  Revision:  $Revision: 1.1.1.1 $                 Email: mogill@rotang.com       |
|  Copyright 1999-2000                         ROTANG -- ZOOM! NOT BOOM!      |
+-----------------------------------------------------------------------------+
| This program is *NOT* yours to use!  This code is made publicly available   |
| for educational and research purposes only.  It, and software derived from  |
| it, are not to be included in or supplied with your program if your program |
| has any use other than its intended research or education.  This entire     |
| banner must accompany any use or derivation of this code.  Any non-research |
| or non-educational uses of this code must be licensed by the author.        |
+-----------------------------------------------------------------------------+
#endif
#if 0
                   BUILDING doall
-----------------------------------------------------------------
	Linux:		cc doall.c -o doall -lpthread
	Solaris:	cc doall.c -o doall -lposix4 -lpthread
#endif

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <pthread.h>

#if !defined(TRUE)
#  define TRUE 1
#endif
#if !defined(FALSE)
#  define FALSE 0
#endif


#define SLEEP(_sec_, _usec_) \
{ \
  struct timespec       sleep_time; \
\
  sleep_time.tv_sec = _sec_; \
  sleep_time.tv_nsec = _usec_ * 1000; \
  nanosleep(&sleep_time, NULL); \
}



typedef  struct
{
  pthread_t		thread;/* Thread of each parallel task */
  pthread_mutex_t	lock;	/* Barrier lock for each task */
  int			busy;	/* Semaphore indicates busy state of task */
  int			iter;	/* Iteration number task is now working on */
} task_ts;

task_ts	*tasks;		/* Control structures for all the tasks */


void body( void *my_id_arg );
void driver( int	niterations,	/* Number of iterations to perform */
	     int	ntasks );	/* Number of tasks to use */


/*==========================================================================*/


/*
 * Program entry point
 */
void main( int   argc,
      char      *argv[])
{
  int	 i;	/* Index across iterations */
  int	 ntasks;	/* Number of tasks to use */
  int	 niterations;	/* Number of iterations of the loop to perform */

  if(argc != 3)
    {
      fprintf(stderr, "Usage:  %s <ntasks> <niterations>\n", argv[0]);
      exit(1);
    }
  ntasks = atoi(argv[1]);
  if(ntasks == 0)
    {
      fprintf(stderr, "%s: Number of tasks (%s) illegal.  Must 1 or more.\n",
	      argv[0], argv[1]);
      exit(1);
    }
  niterations = atoi(argv[2]);
  if(niterations == 0)
    {
      fprintf(stderr, "%s: Number of iterations (%s) illegal.  Must 1 or more.\n",
	      argv[0], argv[2]);
      exit(1);
    }
  if(ntasks > niterations)
    {
      fprintf(stderr, "%s: Clamping number of tasks (%d) to number of iterations (%d).\n",
	      argv[0], ntasks, niterations);
      ntasks = niterations;
    }

  /*===========================================================*/

  tasks = (task_ts *) malloc(sizeof(task_ts) * ntasks);
  if(tasks == NULL)
    {
      fprintf(stderr, "%s: Failure allocating %d task_ts's (size=%d) for tasks.\n",
	      argv[0], ntasks,  sizeof(task_ts));
      exit(1);
    }

  for(i = 0; i < ntasks; i++)
    {
      tasks[i].busy	= FALSE;
      pthread_mutex_init( &(tasks[i].lock), NULL );
      if( pthread_mutex_lock( &(tasks[i].lock) ) )
	fprintf(stderr, "main: Unable to initally lock task %d", i);

      if( pthread_create( &(tasks[i].thread),	/* Handle of thread */
			  NULL,			/* Scheduling attributes (NULL == default) */
			  (void *(*)(void*)) body, /* Function to invoke as thread */
			  (void*) i		/* Argument to thread */
			) != 0)
	fprintf(stderr, "main: Couldn't create thread for task (%d)\n", i);
    }

  driver( niterations, ntasks );
#if !defined(JOIN_THREADS)
  /* If the threads exit, we cannot run the experiment
   * again without restarting all the task threads.
   */
  fprintf(stderr, "And again!\n");
  driver( niterations, ntasks / 2 );
  fprintf(stderr, "All done!\n");
#endif
}



/*
 * Perform the body of the loop
 */
void body( void *my_id_arg )
{
  int	 my_id = (int) my_id_arg;	/* Thread ID number */
  void	*retval = (void*) 0;		/* Return value of task */

  while(TRUE)
    {
      if( pthread_mutex_lock( &(tasks[my_id].lock) ) )
	fprintf(stderr, "body: Unable to block loop body for task %d", my_id);

#if defined(JOIN_THREADS)
      /* If the threads are to exit when they are no longer needed,
       * they are signaled with an impossible iteration number (-1).
       */
      if(tasks[my_id].iter == -1)
	pthread_exit(retval);
#endif
	
      fprintf(stderr, "body: iteration #%d performed by task %d\n", 
	      tasks[my_id].iter, my_id);

      SLEEP(0, my_id * 10000);	/* Simulates variable load per task */

      tasks[my_id].busy = FALSE;
    }
}



void driver( int	niterations,	/* Number of iterations to perform */
	     int	ntasks )	/* Number of tasks to use */
{
  int	 i;		/* Index over iterations */
  int	 taskn;		/* Index into tasks */


  fprintf(stderr, "driver:  niter=%d   ntasks=%d\n", niterations, ntasks);
  /*
   * Loop over all iterations
   */
  for(i = 0; i < niterations; i++)
    {
      int	 tasks_avail;	/* Flag indicates a task is available for work */
      int	 taskn;		/* Index into tasks */

      tasks_avail = FALSE;
      do
	{
	  taskn = 0;
	  while( taskn < ntasks  &&  tasks[taskn].busy )
	    taskn++;
	  if(taskn >= ntasks)
	    SLEEP(0, 10000)	/* Magic retry interval */
	  else
	    tasks_avail = TRUE;
	}
      while( !tasks_avail );

      tasks[taskn].busy = TRUE;
      tasks[taskn].iter = i;

      if( pthread_mutex_unlock( &(tasks[taskn].lock) ) )
	fprintf(stderr, "driver: Unable to release task %d", taskn);
    }



#if defined(JOIN_THREADS)
  /*
   * Join all the threads as they exit.
   */ 
  for( taskn = 0; taskn < ntasks; taskn++ )
    {
      void	*return_status;		/* Status of task upon exit */

      tasks[taskn].iter = -1;		/* Iteration -1 interpreted as exit */
      if( pthread_mutex_unlock( &(tasks[taskn].lock) ) )
	fprintf(stderr, "driver: Unable to release task %d to join it", taskn);
      pthread_join( tasks[taskn].thread, &return_status );
      fprintf(stderr, "Task %d finished with status %d\n", 
	      taskn, (int) return_status);
    }
#else
  /*
   * We've started all the iterations, but we need to block at
   * the end untill they're all done.
   */
  for(taskn = 0; taskn < ntasks; taskn++)
    while( tasks[taskn].busy )
      SLEEP(0, 100000);	/* Magic retry interval */
#endif
}


      


