(Latest Revision: 11/13/2005)

PROTOCOL.CPP



/*

This is my solution to the problem!!  The catch is that I have removed all the
code that does the synchronization.  You can actually compile this, run it,
and watch the fun as all the threads get in each other's way.  Just make a
directory containing protocol.strip.cpp, protocol.strip.h, sem.cpp, and sem.h.
Then compile on a Sun Ultra with the command:

g++ protocol.strip.cpp sem.cpp -lpthread -lposix4

In theory, you could write your solution to the problem by just putting
synchronization code in to the "blanks" I left.  I don't know if that's a very
good idea. Anyway if you read the code maybe it will help you get a good grasp
of where the problem lies.  At the very least you can use parts of it to save
time.  For example you could use my functions that write the user and agent
messages.

*/

/* 
   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 bufferType indexed by
         the set {0,1} of protocols.  */

bufferType pktBuf[2] ;    
   
     /* 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 checking in your main program file. */

int  my_checking ;

/* ################################################## */
/*                         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 * N)
{
  int n = (int) N ;
  int i, packetNum = 0, protocol, delay ;
  do
  {
     protocol = (random() % 2 ) ;

   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
            /* perform synchronization action */
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */

            /* simulate the writing of a packet 
               into the buffer */

     pktBuf[protocol].info.userID = n ;
     pktBuf[protocol].info.packetNum = packetNum ;
     delay = random() % 10 ;
     /*
     if (n==0)  delay = random() % 100;
     else delay = random() % 1000 ;
     */
     for (i=0; i<delay; i++) sched_yield();
     userMessage (n, packetNum, protocol) ;
     packetNum++ ;

   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
        /* perform more synchronization work */
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */

     delay = random() % 10 ;
     /*
     if (n==0)  delay = random() % 100;
     else delay = random() % 1000 ;
     */
     for (i=0; i<delay; i++) sched_yield();
  }
  while (1) ;
}

/* ################################################## */
/*                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 * N)
{ 
  int n = (int) N ;
  int i, packetNum, protocol, userID, delay ;

      /* Pick either protocol to start with. */

  protocol = (random() % 2 ) ;
  do
  {  
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
        /* Do some synchronization work and if certain conditions
           are true then do the block of instructions that follow
        */
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
  /*  if (???????)  */
     { 

   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
           /* Do some additional work associated with 
              synchronization */
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */

       packetNum = pktBuf[protocol].info.packetNum ;
       userID    = pktBuf[protocol].info.userID ;
/*       delay = random() % 1000 ; */
       delay = random() % 100 ;
       /*
       if (n==0)  delay = random() % 100;
       else delay = random() % 1000 ;
       */
       for (i=0; i<delay; i++) sched_yield();
       agentMessage (n, packetNum, userID, protocol) ;
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
          /* More synchronization code */
   /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
     }

/*     else    */
     {  
           /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
           /* This block would get executed instead of the one above
              if there was a reason not to let the agent touch the buffer.
              This block just does some synchronization tasks. /*
          /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
     }
          /* Get set to check the other buffer */
     protocol = (protocol + 1) % 2 ;
/*       delay = random() % 1000 ; */
       delay = random() % 100 ;
     /*
     if (n==0)  delay = random() % 100;
     else delay = random() % 1000 ;
     */
     for (i=0; i<delay; i++) sched_yield();

  }
  while (1) ;
}

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

  init() ;  

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

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

  agent((void *) 1) ;
  exit(0)  ;
}