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:
- randomly chooses Buffer0 or Buffer1,
- copies a "packet" into the chosen buffer,
- writes a message to stdout, and
- increments a private serial number (not shared with the other
user), and
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:
- checks the 'current' buffer,
- if the buffer needs to be copied out and it is not being serviced by the
other agent,
- copies out the buffer, and
- writes a message to stdout.
- makes the other buffer the current buffer.
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();