THE PROBLEM

CS 3750 -- Operating Systems I
Statement of the "Packet Protocol" Concurrent Programming Problem

User0      User1
  |  \    /  |
  |   \  /   |
  |    \/    |
  |    /\    |
  |   /  \   |
  |  /    \  |
Buffer0   Buffer1
  |  \    /  |
  |   \  /   |
  |    \/    |
  |    /\    |
  |   /  \   |
  |  /    \  |
Agent0    Agent1
Write the synchronization for a program that uses four threads in one task to simulate the situation depicted in the diagram above. There are two user threads and two agent threads.

The idea depicted by the diagram can be stated this way: There are two different types of packets, say ethernet packets and token ring packets. User0 and User1 are long-running applications that sometimes need to send a packet out on the ethernet and sometimes need to send a packet out on the token ring. Buffer0 and Buffer1 are two different specialized communication endpoints. Buffer0 is designed for receiving ethernet packets - only ethernet packets, and Buffer1 is exclusively for receiving token-ring packets. When a user thread wants to send a packet, it has to copy the packet into the buffer that corresponds to the packet type.

Agent0 and Agent1 are responsible for servicing Buffer0 and Buffer1, by copying out and then transmitting packets. The agents are not "specialists." Each agent is able to service either buffer. (This might be a performance advantage. Suppose one of the buffers gets two packets in a row, and one of the agents copies out the second packet while the first agent is busy transmitting the first packet.) Agent0 and Agent1 both continually scan both buffers, looking for one that is full and not yet being serviced by the other agent.

Clearly these threads have to synchronize some of their actions with each other. We can't have two users writing data into the same buffer at the same time. We can't have an agent copying a packet out of a buffer while a user is writing a packet into the buffer. We can't have the same packet copied out (and transmitted) twice.

User threads run in an infinite loop. In each iteration of the loop, a user: The "packet" contains only the id number of the user (0 or 1) and the current serial number. To keep the program simple, the threads don't actually put any message data into the buffers.

agents also run in an infinite loop. In each iteration, an agent: In creating the synchronization code, you may find it is convenient to use one or more flags associated with each buffer. For example, user and/or agent threads might read and write flags that indicate a buffer is 'full' or 'in-use'. (However, remember that insuring correct operation when multiple threads access shared flags may be a critical section problem.)

Your program must carefully prevent concurrent threads from corrupting the contents of shared variables. On the other hand, a thread must never unnecessarily prevent another thread from making progress.

To enforce exclusive access to shared buffers, you must use semaphores implemented by sem.h and sem.c. The exact manner in which you employ the semaphores is for you to decide.

users will write a specific output immediately after they place a packet in a buffer. Example:
Copy IN:        Packet #0 of user #1 into buffer #0
This line of output must be written in such a way that it is guaranteed to appear on the screen before the message of an agent appears on the screen, saying the agent has copied this packet out of the buffer.

Agents have to cooperate to make sure that only one agent copies out each packet. Each agent can and will copy packets out of either buffer, in effect 'competing' with the other agent to copy out as many packets as possible as soon as possible. The agents must be programmed in such a way that if one agent is delayed for some reason, the other agent must automatically 'take over' and do all the work, copying all packets from both buffers. If one agent is busy working with the packet from one buffer, and if there is a packet in the other buffer that needs to be copied, then there must be nothing to prevent the other agent from immediately working on the other buffer. Agents will write a specific output immediately after they copy a packet from a buffer. Example:
agent #1 copies packet #145 of User #0 from buffer #1.
This line of output must be written in such a way that it is guaranteed to appear on the screen before the message of a user appears on the screen saying it has placed a new packet in this buffer.

Note: User and agent messages must be formatted so that the packet, user, and buffer numbers all line up in columns. This will make it a lot easier to check outputs.

To test your program, you will use code in critical places that delays each thread by a random amount of time. We do this by generating a random integer n, and then making the thread execute

	for (i = 0; i < n; i += 1)  sched_yield();