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) ;
}