John Sarraille

THE HANDOUT!

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

User0      User1
  |  \    /  |
  |   \  /   |
  |    \/    |
  |    /\    |
  |   /  \   |
  |  /    \  |
Buffer0   Buffer1
  |  \    /  |
  |   \  /   |
  |    \/    |
  |    /\    |
  |   /  \   |
  |  /    \  |
Agent0    Agent1
Write a C program that uses four threads in one task to simulate the situation depicted in the diagram above. There must be two USER threads and two AGENT threads.

USER threads run an infinite loop. In each iteration of the loop, a USER randomly chooses Buffer0 or Buffer1, increments a private serial number, and copies a "packet" into the chosen buffer. The packet contains the id number of the USER (0 or 1) and the current serial number. (This is simulates a pair of processess or threads sending what might be long messages by placing them in a "mailbox." To keep the programming simpler, we do not actually write long messages.)

AGENTS also run an infinite loop, continually examining both buffers. The AGENTS cooperate to copy out each packet that has been copied in by a USER. (This simulates two threads sharing a kernel task that takes messages out of a sender's mailbox and delivers them to the intended recipient's mailbox.

Alternatively, you can think of this set-up as a form of producer consumer problem, where there are two producers, two buffers, and two consumers.

You may find it is convenient to use one or more flags in each buffer. Threads may read and write flags in order to communicate. (However you will need to use semaphores for some of the communication/synchronization actions.)

Your program must carefully prevent concurrent threads from corrupting the contents of shared variables. For example, it is an error if a USER overwrites a part of a packet that has not yet been copied out by an AGENT. On the other hand, a thread must never unnecessarily prevent another thread from accessing a buffer.

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 must write a specific output when 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 an AGENT prints a message saying it 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, competing with the other AGENT to copy out as many packets as possible as soon as possible. If either AGENT stops copying packets, the other AGENT must automatically do all the work, copying all packets from both buffers. If one AGENT is busy copying a packet from one buffer, and if there is a packet in the other buffer that needs to be copied, then the other AGENT must immediately begin copying from the other buffer. AGENTS must write a specific output when 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 a USER prints a message saying it has placed the next packet in this buffer.

Note: Please format USER and AGENT messages 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 must insert in critical places code that delays each thread by a random amount of time. You can do this by generating a random integer n, and then making the thread execute

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