SOURCE FILE: protocol.cpp


/* 
   The set-up:  two user processes write packets to two protocol
   buffers.  Each user writes to both buffers. Two agents of
   transmission serve the two protocol buffers.  Each serves both
   buffers.
*/

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

using namespace std;

      /* 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 ;

      /* Shared var pktBuf is an array of two bufferType indexed
         by the set {0,1} of protocols.  These are the two packet
         buffers. Users copy packets into a buffer, and agengs
         copy packets out of them.  */
bufferType pktBuf[2] ;    
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
        /* If you want semaphore(s), flag variable(s), or 
           status variable(s) associated with each pktBuf 
           element, you can add them to the definition of the 
           bufferType in the header file protocol.h */
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
   
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
        /* Here declare any additional global semaphore, flag, or
           status variable(s) that may be needed - besides
           whatever such variables are defined as fields in the
           buffers */
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */

     /* global variables to represent threads created
        dynamically. */
pthread_t user_t[2], agent_t[2] ;

      /* Set checking to 1 if you want messages from the
          functions below and from sem.cpp, else set to 0.  */
int  checking ;      
                     
      /* Use this flag to turn on or off whatever checking you
         decided to build in to this (main program) file. */
int  my_checking ;

    /* This determines how many packets each user can send before
       the simulation is terminated. Vary this to get longer or
       shorter test runs.*/
const int maxNumPackets = 25 ;

     /* A data type - a struct with an int field 
        to represent a thread ID. */
struct threadIdType
{
  int id ;
};


/* ################################################## */
/*                         init                       */
/* ################################################## */
void init() 
{
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
          /* (Initialize the flags and semaphores used for 
              synchronization.) */
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */

      /* Set checking to 1 if you want messages from the
          functions below and from sem.cpp, else set to 0.  */

  checking = 0 ;  
  my_checking = 0 ;  

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

      /* initialize random number generator */ 
  srandom(time((time_t *) 0));
}

/* ################################################## */
/*                  userMessage                       */
/* ################################################## */
void userMessage (int n, int packetNum, int protocol)
{
   pthread_mutex_lock(&stdoutLock) ;
   cout << "Copy IN:        Packet #" << packetNum 
   << " of USER #" << n << " into buffer #" << protocol 
    << "." << endl ; 
   pthread_mutex_unlock(&stdoutLock) ;
}

/* ################################################## */
/*                       user                         */
/* ################################################## */
void * user (void * idPtr)
{
  int me = ((threadIdType *) (idPtr))->id ;
  int i, packetNum = 0, protocol, delay ;
  do
  {
         /*
               This program is supposed to simulate a situation
               in which the user decides to send a packet every
               so often.  There are two kinds of packets. It's
               equally likely that the user will choose to send
               either kind of packet.  So we randomly choose one
               of two numbers below (0 or 1), and the number
               determines which type of packet.
         */

     protocol = (random() % 2 ) ;

   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
            /* perform synchronization action to get 
               exclusive access to pktBuf[protocol]. */
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */

            /* simulate the writing of a packet 
               into the buffer */
     pktBuf[protocol].info.userID = me ;
     pktBuf[protocol].info.packetNum = packetNum ;

        /* Choose a random small delay that is on the same
           scale for both users. */
     delay = random() % 10 ;

        /* Below is a possible alternate way to set the delay 
           that tends to make user #1 run at a slower rate than
           user #0  */
        /*
            if (me==0)  delay = random() % 100;
            else delay = random() % 1000 ;
        */

       /* NOW WE EXECUTE THE DELAY. THE DELAY SIMULATES THE TIME
          REQUIRED TO COPY WHAT MIGHT BE A LARGE PACKET INTO THE
          BUFFER.  IT COULD BE A LONG DELAY, OR A SHORT DELAY. */
     for (i=0; i<delay; i++) sched_yield();

       /* Now write the message to the screen that announces 
          that the packet has been placed in the buffer. */
     userMessage (me, packetNum, protocol) ;

     packetNum++ ;

   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
        /* perform more synchronization work here so an agent
           will be able to tell that there's a packet ready to be
           copied out of pktBuf[protocol].   */
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */

     delay = random() % 10 ;
        /*
            if (me==0)  delay = random() % 100;
            else delay = random() % 1000 ;
        */

       /* THIS DELAY SIMULATES THE TIME THAT ELAPSES
          BEFORE THE USER HAS ANOTHER PACKET READY TO 
          SEND. IT COULD BE A LONG DELAY.  */
     for (i=0; i<delay; i++) sched_yield();
  }
  while (packetNum < maxNumPackets) ;
}

/* ################################################## */
/*                agentMessage                        */
/* ################################################## */
void agentMessage (int n, int packetNum,
                   int userID, int protocol)
{
   pthread_mutex_lock(&stdoutLock) ;
   cout << "AGENT #" << n << " copies packet #" << packetNum 
   << " of USER #" << userID << " from buffer #" << protocol
   << "." << endl ;
   pthread_mutex_unlock(&stdoutLock) ;
}

/* ################################################## */
/*                        agent                       */
/* ################################################## */
void * agent (void * idPtr)
{ 
  int me = ((threadIdType *) (idPtr))->id ;
  int i, packetNum, protocol, userID, delay ;

      /* Pick either protocol to start with. */

  protocol = (random() % 2 ) ;
  do
  {  
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
       /* There may be no full buffers available now.  Put code
          here to allow the agent to wait, if necessary, and then
          acquire exclusive access to a full buffer. The acquired
          buffer may be either buffer #0 or #1.  Therefore, the
          code that goes here may change the value of the
          variable "protocol" from the value it had before. */
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */

            /* Now do the copy out on a buffer that
               requires service. */
       packetNum = pktBuf[protocol].info.packetNum ;
       userID    = pktBuf[protocol].info.userID ;

             /* delay = random() % 1000 ; */
       delay = random() % 100 ;
        /*
            if (me==0)  delay = random() % 100;
            else delay = random() % 1000 ;
        */

       /* THIS DELAY SIMULATES THE WORK REQUIRED TO 
          COPY OUT WHAT MIGHT BE A LARGE PACKET.  IT
          COULD BE A LONG DELAY.  */
       for (i=0; i<delay; i++) sched_yield();

            /* Now write the message to the screen announcing
               that the agent copied a packet out of a buffer. */
       agentMessage (me, packetNum, userID, protocol) ;

   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
           /* Put some code here that allows the users to
              find out that it is now OK to copy a new
              packet into the pktBuf[protocol]. */
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */

             /* delay = random() % 1000 ; */
       delay = random() % 100 ;
        /*
            if (me==0)  delay = random() % 100;
            else delay = random() % 1000 ;
        */

       /* THIS DELAY SIMULATES THE WORK REQUIRED TO 
          TRANSMIT ONTO THE NETWORK WHAT MIGHT BE A 
          LARGE PACKET.  IT COULD BE A LONG DELAY.  */
     for (i=0; i<delay; i++) sched_yield();

          /* Make sure that the first protocol to be tried next
             time is not the same one we just serviced. This
             prevents the possibility of starvation of one of the
             protocols. */
     protocol = (protocol + 1) % 2 ;


  }
  while (packetNum<maxNumPackets-1) ;
}

/* ################################################## */
/*                        main                        */
/* ################################################## */
main() 
{ 
  int thrNum ;

  init() ;  

  threadIdType * idPtr ;

  for (thrNum=0; thrNum<2; thrNum++)
  {
    idPtr = new threadIdType ;
    idPtr->id = thrNum ;
    if (0!=pthread_create(&user_t[thrNum], NULL, user, (void *) idPtr ))
    {  cout << "THREAD CREATION FAILURE!" << endl ;
       exit(-1) ; }
     
    if (0!=pthread_detach(user_t[thrNum]))
    {  cout << "THREAD DETACHMENT FAILURE!" << endl ;
       exit(-1) ;}
  }

  idPtr = new threadIdType ;
  idPtr->id = 0 ;
  if (0!=pthread_create(&agent_t[0], NULL, agent, (void *) idPtr ))
  {  cout << "THREAD CREATION FAILURE!" << endl ;
     exit(-1) ; }
     
  if (0!=pthread_detach(agent_t[0]))
  {  cout << "THREAD DETACHMENT FAILURE!" << endl ;
     exit(-1) ;}

  idPtr = new threadIdType ;
  idPtr->id = 1 ;
  agent((void *) idPtr) ;
  exit(0)  ;
}