Lab 8: Synchronization
The goal of this lab is to practice using locks and semaphores to protect access to critical sections. You will use a lock in a few places in relation to a nested loop to observe the timing that results from different granularities of locking. You will also use a pair of semaphores to control the timing back and forth between two threads.
Preliminaries
Set up a bare repository on stu.cs.jmu.edu based on the instructions
in the CS 361 Submission Procedures,
using the name lab8-synch.git
.
Implementation requirements: Protecting critical sections
For this first part, your goal is to protect a critical section created by a nested loop. The inner-most loop adds 1 to a shared variable then subtracts 1 from it. The end result should be 0 if this critical section is protected. Your task is to use a lock as needed. You will also need to set up two threads to run concurrently and perform this calculation.
- Edit the
run()
function inmutex.c
to create then join two threads, each of which run therunner()
function from the same file. - Use the lock to synchronize access to the shared variable in
runner()
. The base distribution uses aNULL
pointer forshared
. You need to change this to get the reference from the thread arguments. Note that you can place the locking and unlocking function calls in multiple places. One of the unit tests includes a timing constraint that you must pass: Correct placement of the lock will result in parallel execution that is at least three times as fast as the sequential execution, but no more than six times as fast.
Due to the unpredictable nature of threads and the workload on
stu
, it is possible that the timing tests might fail
occasionally. If you run it again without recompiling and it passes,
then that is fine.
For the second part, you will use two semaphores to control the timing
of two threads to produce "PING!"
and "PONG!"
messages back and forth several times. For a correct implementation,
these messages should alternate every line; there should never be
multiple lines in a row of the same message. The semaphores can ensure
this happens.
- Edit the
ping()
function inpingpong.c
to create and initialize the semaphores. You will need to determine the initial values. This thread should then create thepong()
thread, passing references to both semaphores, then enter a loop to alternate messages. Complete the rest of the code based on the embedded comments. - Next, implement the
pong()
thread based on the comments. This code should be very similar to the loop in thepong()
function.
In the final implementation of this lab, the "PING!
message
should always appear first. After the alternating messages, the
"PONG: Game over"
message should appear before the
"PING: Game over"
message. The timing of this last
alternation arises by having the parent thread join the child.
For completeness, be sure to clean up the semaphores with
sem_close()
.