SOURCE FILE: robotShell.cpp


/*

In this version of the program, The mother creates a specific
number of robots.  Each is randomly assigned a direction of
travel.  After creating the robots, the mother blocks "forever"
by waiting on a semaphore that has been initialized to 0.

Each robot remains alive and repeatedly crosses the hall in its
assigned direction.  In other words, after crossing the hall each
time, it is in effect "re-born" back at its starting point,
whereupon it starts another crossing.  It is as if the robot
exited, and at the same time the mother created a new robot to
take its place.

You have to do ctrl-C to stop this program, else it runs
"forever".

The delays in this particular version of the program allow the
program to deadlock easily if no protocol is added to arbitrate
between north- and south-bound robots. That is undoubtedly what
will happen if you just compile and run this program.


Suppose a protocol is added that assures 

1. only one direction of traffic at a time is allowed in the hall, 
2. the (text book) progress requirement holds, 
3. the (text book) bounded waiting requirement does *not* hold.

In that case the delays here are likely to starve south-bound
robots.

Therefore the delays here should be quite useful for
troubleshooting several stages of the development of an advanced
protocol for synchronizing the robots.

Nonetheless, the programmer assigned to adding a synchronizing
protocol should experiment with a variety of configurations of
delay in order to fully test the protocols.  In my testing of
your program for the purpose of assigning a grade, I may very
well alter the delays to see if I can "break" your code.

*/

#include <iostream.h>
#include <stdlib.h>
#include <sched.h>
#include <time.h>
#include <pthread.h>
#include "sem.h"

#define NORTH 0
#define SOUTH 1
#define CHILD 0
#define MOTHER 1

#define NUM_CELLS 16
#define NUM_ROBOTS 25

extern sim_semaphore create_sim_sem(int) ;
extern void wait_sem (sim_semaphore) ;
extern void signal_sem (sim_semaphore) ;
extern long random(void);

	/* WHEN DEBUGGING, WE NEED TO USE THIS LOCK TO GET
	   INTELLIGIBLE PRINTOUTS OF WHAT THE VARIOUS THREADS ARE
	   DOING.  WARNING:  DON'T CHANGE HOW THIS IS DONE UNTIL
	   YOU'VE THOUGHT A *WHOLE* LOT ABOUT THE CONSEQUENCES.
	   */
pthread_mutex_t stdoutLock ;

int checking ;   /* JUST FOR DEBUGGING */

		/* GLOBAL VARIABLE TO REPRESENT THREADS CREATED
		   DYNAMICALLY.  */
pthread_t thr[NUM_ROBOTS] ; 

sim_semaphore blockMother ; /* SOMETHING TO FORCE THE MOTHER TO
			       BLOCK AFTER CREATING THE CHILD
			       THREADS (ROBOTS). */

		/* SEMAPHORES TO GUARD THE CELL DIVISIONS OF THE
		   CORRIDOR */
sim_semaphore hall_cell[NUM_CELLS] ; 

/* .. ADD THE OTHER DECLARATIONS YOU MAY NEED HERE .. */

/* ################################################## */
/*                         init                       */
/* ################################################## */
void init() 
{ 
  int i ;

  srandom(time((time_t *) 0)); /* INITIALIZE RANDOM NUMBER
                                  GENERATOR */

  checking = 0 ; /* JUST FOR DEBUGGING: CHANGE TO 1 IF YOU WANT
		    MESSAGES. */

	          /* INITIALIZE THE SPECIAL MUTEX FOR LOCKING
		     STDOUT */

  if ( 0!=pthread_mutex_init(&stdoutLock, NULL) )
  {  cout << "MUTEX INITIALIZATION FAILURE!" << endl ;
     exit(-1) ;}

  blockMother = create_sim_sem(0) ; /* INITIALIZE "blockMother"
				       TO 0 TO FORCE A BLOCK. */

		/* INITIALIZE THE SEMAPHORES GUARDING THE CELLS
		   IN THE HALL. */
  for (i=0;i<NUM_CELLS;i++) hall_cell[i]=create_sim_sem(1);


        /* .. HERE, PLACE OTHER INITIALIZATIONS YOU MAY NEED ..
              */

}
/* ################################################## */
/*                    DelayAsMuchAs                   */
/* ################################################## */
void delayAsMuchAs (int limit)
{
  int time, step;
  time=(int)random()%limit;
  for (step=0;step<time;step++) sched_yield() ;
}
/* ################################################## */
/*                    SetDirection                    */
/* ################################################## */
void setDirection(int direction,
                  int & first, int  & last, int  & incr)
{
  if (direction==NORTH)
       { first=0; last=15; incr=1; }
  else {first=15; last=0; incr= -1;}
}
/* ################################################## */
/*                    CheckInToHall                   */
/* ################################################## */
void checkInToHall(int direction)
{

/* .. perform synchronization actions .. */

}
/* ################################################## */
/*                    CheckOutOfHall                  */
/* ################################################## */
void checkOutOfHall(int direction)
{

/* .. perform synchronization actions .. */

}
/* ################################################## */
/*                    PrintStartMsg                   */
/* ################################################## */
void printStartMsg(int serNum, int direction)
{
  pthread_mutex_lock(&stdoutLock) ;

  if   (direction==NORTH)
       cout << "Child " << serNum << " starts north." << endl ;
  else cout << "Child " << serNum << " starts south." << endl ;

  pthread_mutex_unlock(&stdoutLock);
}
/* ################################################## */
/*                    PrintFinishMsg                  */
/* ################################################## */
void printFinishMsg(int serNum, int direction)
{
  pthread_mutex_lock(&stdoutLock) ;

  if   (direction==NORTH)
       cout << "Child " << serNum << " finishes north." << endl ;
  else cout << "Child " << serNum << " finishes south." << endl ;

  pthread_mutex_unlock(&stdoutLock);
}
/* ################################################## */
/*                   PassThroughHall                  */
/* ################################################## */
/*
      This function simulates a robot travelling from one end of
      the hall to the other.  The values of first, last, and incr
      determine the direction of travel.  The robot reserves the
      first cell it must travel through.  It then loops doing
      (reserve next cell ; release previous cell) until the last
      cell has been reserved.  Finally it releases the last cell.
      This simulates a walking kind of action.  The threads
      executing this protocol are synchronized up to a point:  No
      two robots can be in the same cell at the same time.  Thus
      collisions are impossible.  Two robots cannot enter the
      same end of the hallway at the same time.  Also, there is
      no way for one robot to pass another robot.  This applies
      whether the robots are going in the same or different
      directions.
*/
void passThroughHall(int first, int last,
                     int incr,  int serNum, int direction)
{
  int i;
  
  delayAsMuchAs(100);
  wait_sem(hall_cell[first]);
  printStartMsg(serNum,direction);
  for (i=first;i!=last;i=i+incr)
  {

     /*
        This delay makes robots go thru slowly.  This has a
	tendency to keep the hall non-empty all of the time,
	unless something special is done in the protocol to cause
	the hall to "drain."
     */

    delayAsMuchAs(1000);
    wait_sem(hall_cell[i+incr]);
    signal_sem(hall_cell[i]);
  }
  printFinishMsg(serNum,direction);
  signal_sem(hall_cell[last]);
}
/* ################################################## */
/*                    DoRobotNorth                    */
/* ################################################## */
void * doRobotNorth(void * serNumPtr)
{
  int serNum = (int) serNumPtr ;
  int first, last, i, incr, time, step ;

  setDirection(NORTH, first, last, incr);
  while (true)
  {
    checkInToHall(NORTH) ;
    passThroughHall(first, last, incr, serNum, NORTH);
    checkOutOfHall(NORTH);
  }
  pthread_exit ((void *) 0); 
}
/* ################################################## */
/*                    DoRobotSouth                    */
/* ################################################## */
void * doRobotSouth(void * serNumPtr)
{
  int serNum = (int) serNumPtr ;
  int first, last, i, incr, time, step ;

  setDirection(SOUTH, first, last, incr);
  while (true)
  {
      /* 
          The delay below makes south-bound robots slow to
	  attempt to check in to the hall.  Thus, north-bound
	  robots are likely to start using the hall first.
	  Suppose the protocol is doing something to prevent
	  traffic in both directions from occupying the hall
	  simultaneously.  In that case, once occupied by
	  north-bound robots, the hall is likely to remain
	  occupied by north-bound robots.  The reason is that
	  other delays have been added which force most robots to
	  pass through the hall very slowly.  Thus the hall is
	  not likely ever to be emptied of north-bound robots,
	  unless the protocol does something special to force the
	  hall to drain.
      */

    delayAsMuchAs(1000);
    checkInToHall(SOUTH) ;
    passThroughHall(first, last, incr, serNum, SOUTH);
    checkOutOfHall(SOUTH);
  }
  pthread_exit ((void *) 0); 
}
/* ################################################## */
/*                         MAIN                       */
/* ################################################## */
void main() 
{
  int serNum, direction ;

  init();

  for (serNum=0; serNum < NUM_ROBOTS; serNum++)
  {
    direction=(int)random()%2;

    if (direction == NORTH)
    {
       cout << "Creating robot #" << serNum << " ... " << endl ;
       cout << "Direction is North" << endl ;
       if    ( 0 != pthread_create
             ( &thr[serNum], NULL, doRobotNorth, (void *) serNum ) )
       { cout << "THREAD CREATION FAILURE!" << endl ; exit(-1) ; }

       if ( 0!=pthread_detach(thr[serNum]) )
       {  cout << "THREAD DETACHMENT FAILURE!" << endl ; exit(-1) ;}
    }

    else
    {
       cout << "Creating robot #" << serNum << " ... " << endl ;
       cout << "Direction is South" << endl ;
       if    ( 0 != pthread_create
             ( &thr[serNum], NULL, doRobotSouth, (void *) serNum ) )
       { cout << "THREAD CREATION FAILURE!" << endl ; exit(-1) ; }

       if ( 0!=pthread_detach(thr[serNum]) )
       {  cout << "THREAD DETACHMENT FAILURE!" << endl ; exit(-1) ;}
    }

  }

  wait_sem(blockMother);

  exit(0) ;
}