SOURCE FILE: foodPass2.cpp



/*     

In this program the server puts dishes full of food on one end of
a table and diners pass the dishes down to the other end of the
table where they are collected by the busser.

This is a more "advanced" version of the program where there are
two diners between each pair of trivets, rather than just one.

The job of each diner is to loop these actions:

* I become ready to "experience" the next dish, (basically "I
  become hungry")

* I acquire the next dish on the trivet next to me in direction
  "north" (exclusively)

* IF the dish contains food THEN I eat some of the food,

* IF    the diner opposite me has already taken food from this dish
  THEN  when available, I place the dish on the trivet next to me
        in direction south.

* IF    the dish I just experienced contains the check THEN I exit,
  ELSE  I go back to the start of this loop.

The job of the server is to serve dishes by placing them one
after another onto trivet 0 (the northernmost trivet) and then
exit.

The job of the busser is to remove dishes from the last trivet
(southernmost) one after another until removing the check.

The threads implementing the server, busser, and diners have to
coordinate.  Their goals are to

1. avoid as much busy waiting as possible 

2. achieve as much parallel processing as possible

3. avoid the possibility that a person could acquire the same dish twice

4. avoid the possiblity that a person could "serve from an empty dish"

5. avoid the possibility that a person could place one dish on top of another.

6. make sure everyone gets finished eating and the check gets paid.

7. make sure that the only threads which are involved in
   regulating access to a trivet are the threads that either a)
   currently are using the trivet, b) currently are trying to get
   access to the trivet (in entry code), or c) currently are
   releasing the trivet (executing exit code):

Your grade depends on how well you achieve these goals.

*/

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

using namespace std ;

/* ######################################## */
/*             Global Variables             */
/* ######################################## */

//const int numTrivets = 6 ;
const int numTrivets = 3 ;
const int numDiners = 2*(numTrivets-1);
const int maxDishNames = 13 ;
//const int numDishNames = 13 ;
const int numDishNames = 5 ;
const int west = 0 ;
const int east = 1 ;

      /* When trivet[i]==j it means that dish #j is on trivet #i.*/

int trivet[numTrivets] ;

      /* dishName[j] is the name of dish #j */

string dishName[maxDishNames];



/* Here declare all the semaphores and other variables you want
   to use for synchronization */




      /* child_t are global variables to represent the dynamically-created threads. */

pthread_t ** diner_t ;
pthread_t server_t ;

/* ######################################## */
/*      "Special"   Global Variables        */
/* ######################################## */

          /* Code in sem.cpp "expects" the two variables below to
	     be here.  This particular program does not use
	     "checking." */

         /* "Checking" is just a flag that you set to 1 if you
	    want lots of debugging messages and set to 0
	    otherwise.  The semaphore code in sem.cpp imports
	    "checking".  Therefore the semaphore operations will
	    write lots of messages if you set checking=1.  */

int checking ; 

      /* In some programs, we use the "stdoutLock" variable
	 declared below to get intelligible printouts from
	 multiple concurrent threads that write to the standard
	 output.  (There has to be something to prevent the
	 output of the threads from interleaving unintelligibly
	 on the standard output, and we can't use semaphores if
	 the semaphore code is writing messages too.)

         To print a message to standard output, a thread first
	 locks standard output, then writes, then unlocks
	 standard output.  See files sem.cpp or conc.cpp for
	 examples of code that write messages in this manner.

         WARNING:  DON'T change how the locking of standard
	 output is done until you've thought a WHOLE lot about
	 the consequences.  In particular, using semaphores to do
	 the job of stdoutLock can cause "infinite recursion"
	 under certain circumstances.  The reason is that the
	 semaphore code itself imports "stdoutLock" and writes
	 messages when the "checking" variable is set to 1. */

pthread_mutex_t stdoutLock ;

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

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

  checking = 0 ;

       /* Initialize the "special lock" that is used only to get
	  exclusive access to the screen. */

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

/* Here initialize your semaphores and other variables you are using for
   synchronization. */






      /* Initialize the trivets to indicate that each contains
	 "no dish." */

  for (index=0; index<numTrivets; index++) 
  {
    trivet[index] = 0;
  }

    /* Allocate an array of numDiners/2 pointers to pthread_t and make
       diner_t a pointer to the base of that array.

        diner_t
         |
         |
         V
       -----
       |   | ----> pthread_t
       -----
       |   | ----> pthread_t
       -----
       |   | ----> pthread_t
       -----
       |   | ----> pthread_t
       -----

       This makes "diner_t" equivalent to the *name* of an array of pointers
       to pthread_t. */

  diner_t = new pthread_t * [numDiners/2] ;

     /* For each row i of diner_t (diner_t[i]), allocate an array
	of two pthread_t's and make diner_t[i] a pointer to the
	base element of the array.  

        diner_t        
         |
         |
         V
       -----      ---------
       |   | ---->|pth|pth|
       -----      ---------
       |   | ---->|pth|pth|
       -----      ---------
       |   | ---->|pth|pth|
       -----      ---------
       |   | ---->|pth|pth|
       -----      ---------

        This makes diner_t in effect the name of a 2D array of pthread_t's,
	having numDiners/2 rows and two columns.  */

  for (index=0; index<(numDiners/2); index++) 
      diner_t[index] = new pthread_t[2] ;

 /* Give some mnemonic names to the dishes.  The first name is
    used for an empty trivet.  The last name denotes the check
    (bill) for the meal.  This is coded so no changes are needed
    here as long as the value of "numDishNames" is between 2 and
    13. */

  dishName[0]="no dish";
  dishName[1]="vegetable soup" ;
  dishName[2]="bread and butter" ;
  dishName[3]="beets and chickpeas" ;
  dishName[4]="hardboiled eggs" ;
  dishName[5]="calf tongue" ;
  dishName[6]="baked potato" ;
  dishName[7]="string beans" ;
  dishName[8]="rack of lamb" ;
  dishName[9]="salad" ;
  dishName[10]="coffee" ;
  dishName[11]="flan" ;
  dishName[numDishNames-1]="check" ;
}

/* ################################################## */
/*                    DelayAsMuchAs                   */
/* ################################################## */
void delayAsMuchAs (int limit)
{
  int time, step;
  time=(int)random()%limit;
  for (step=0;step<time;step++) sched_yield() ;
}

/* ################################################## */
/*                       Server                       */
/* ################################################## */

/* The mother thread spawns a child thread that executes this
   function.  This function carries out the job of the server at
   the restaurant. */

void * Server(void * ignore)
{

  int i, j, delayLimit=100 ;

  for (i=1; i<numDishNames; i++)
  {

        /* I delay a random time before I "feel like" placing
	   another dish on the table.*/

    delayAsMuchAs(delayLimit);

      /* When the trivet is available, I place the dish on trivet
	 #0. */

       /* Here do a synchronization task.  One thing you need to
	  do is be sure that you are not going to place a dish on
	  a trivet that alreay has a dish on it.  *DO NOT* just
	  busy-wait until you see that the trivet is empty. */





    trivet[0]=i; /* put dish #i onto trivet #0. */

    pthread_mutex_lock(&stdoutLock) ;
    cout << "Server places " << dishName[trivet[0]] 
         << " on trivet #0." << endl ;
    pthread_mutex_unlock(&stdoutLock);

       /* Here you may want to a synchronization task --
	  something that "opens the door" for diners #0 and #1 to
	  get access to the new dish. */



  }
}

/* ################################################## */
/*                         Diner                      */
/* ################################################## */

/* The mother thread spawns child threads that execute this
   function.  This function carries out the job of one of the
   diners at the restaurant. */

void * Diner(void * postnPtr)
{

       /* Type cast the parameter to recover "position" -- which
	  tells me the position at which I am seated at the
	  table. */

  int position = (int) postnPtr ;
  int i, j, delayLimit=100 ;
  int I_pass = 0 ;

  for (i=1; i<numDishNames; i++)
  {

        /* I delay a random time before I "feel like" picking up
	   the next dish.*/

    delayAsMuchAs(delayLimit);
    
      /* When available, I pick up the next new dish on my
	 "north" side.  The number of the trivet is position/2.
	 The "geometry" of the table is as diagrammed below.

                               EAST
                        diner        diner
                          1            3
(NORTH) Server   T               T            T     Busser (SOUTH)
                        diner        diner          
                          0            2
                                WEST

*/

         /* wait for a new dish to appear -- postion/2 is the
	    trivet number and position%2 determines west versus
	    east. */

     /* After some sequence of synchronization actions you will
	do here, you find yourself with a new dish before you and
	a spoon in your hand for serving */





      /* I declare what I am doing */

    pthread_mutex_lock(&stdoutLock) ;

    cout << "Diner number " << position/2 ;
    if (position%2 == west) cout << " west "; else cout << " east ";
    if (i<numDishNames-1) cout << "enjoys ";
    else cout << " examines " ;
    cout << dishName[trivet[position/2]] << "." << endl ;
    pthread_mutex_unlock(&stdoutLock);

	/* I delay a random time to simulate the time it takes
	   for me to serve myself some of what is on the dish --
	   or look at the check. */

    delayAsMuchAs(delayLimit);

        /* Now "eat" or "examine." */




        /* I have to pass the plate if the diner opposite me has already
	   taken some of the food off the dish. */

      
        /* Here I figure out if the diner across from me has
	   already accessed the current dish.  If so, then I am
	   the second and it is my job to pass the dish down to
	   the next trivet.  Accordingly, I will set I_pass to 1
	   here if I am the second to access the dish. */


        /* Do any necessary synchronization tasks here. */


   
        /* If I'm supposed to pass down the plate then I wait for
	   the trivet to the south to become empty. */

    if (I_pass) 
    {
        /* Do any necessary synchronization tasks here. */


           /* Tell the world what I'm doing. */
      pthread_mutex_lock(&stdoutLock) ;
      cout << "Diner number "<< position/2 ;
      if (position%2 == west) cout << " west"; else cout << " east";
      if ( (i==numDishNames-1) && (position/2==numTrivets-2) ) cout << " pays check and" ;
      cout << " moves "  
           << dishName[trivet[(position/2)]] << " from trivet #"
           << position/2 << " to trivet #" << (position/2)+1 << "." << endl;
      pthread_mutex_unlock(&stdoutLock);

           /* transfer the dish on my north side to trivet on my south side.
	   */

      trivet[(position/2)+1]=trivet[position/2] ;

          /* mark trivet on north side as empty */

      trivet[position/2]=0;

         /* Do any necessary synchronization tasks here. */

    } /* end of actions performed by the passer */
  } /* end of main for-loop  */
} /* end of Diner function */

/* ################################################## */
/*                       Busser                       */
/* ################################################## */
/*

     The mother thread spawns children and then executes this function.  This
     is convenient because this function should be the last to exit.  This
     function carries out the job of the busser at the restaurant.

*/
void * Busser (void * ignore)
{

  int i, j, delayLimit=100 ;

  for (i=1; i<numDishNames; i++)
  {

        /* I delay a random time before I "feel like" bussing another
	   dish.*/

    delayAsMuchAs(delayLimit);

      /* When another dish is on the trivet to my north I remove it. */

         /* Do any necessary synchronization tasks here. */

    pthread_mutex_lock(&stdoutLock) ;
    cout << "Busser removes "
         << dishName[trivet[numTrivets-1]] << " from trivet #"
	 << numTrivets-1<< "." << endl ;
    pthread_mutex_unlock(&stdoutLock);

    trivet[numTrivets-1]=0; // remove the dish.

         /* Do any necessary synchronization tasks here. */

  }
}

/* ################################################## */
/*                         Main                       */
/* ################################################## */
main() 
{ 
  init(); 

  cout << endl << endl;
  cout << "Welcome to the restaurant!" << endl ;
  cout << numDiners << " will be dining." << endl ;
  cout << "The meal will consist of " << numDishNames-2 << " dishes." << endl;
  cout << "Bon appetite!" << endl ;
  cout << endl << endl;

  int i;

  for (i=0; i<numDiners; i++)
  {

     if (0!=pthread_create(&diner_t[i/2][i%2], NULL, Diner, (void *) i))
        {cout << "THREAD CREATION FAILURE!" << endl; exit(-1) ;}
     
     if (0!=pthread_detach(diner_t[i/2][i%2]))
        {cout << "THREAD DETACHMENT FAILURE!" << endl ; exit(-1) ;}
  }

     if (0!=pthread_create(&server_t, NULL, Server, (void *) 0))
        {cout << "THREAD CREATION FAILURE!" << endl; exit(-1) ;}
     
     if (0!=pthread_detach(server_t))
        {cout << "THREAD DETACHMENT FAILURE!" << endl ; exit(-1) ;}
	
  Busser((void *)0) ;

  cout << endl << endl;
  cout << "Thank you for coming!" << endl ;
  cout << endl << endl;

}