Released Friday, February 4, 2011
Due Friday, February 25, 2011, 11:59 PM
Introduction |
An angry mob goes after the dining philosophers, who escape into a dark alley in an unfrequented part of the town and call for reinforcements (i.e., we may see more than 5 philosophers). They find an old soup kitchen, 5 bowls, and a coin. They decide to adapt to a new lifestyle and put the bowls on a table. A philosopher now goes through the sequence:
while(1) { result = coin->flip(); if(result == COIN_HEAD){ table->work() } else{ table->eat() } Think() }Where coin (of type
Coin
) and table (of typeTable
) are shared objects that encapsulate the system's shared state. Only one philosopher can flip the shared coin at a time; flipping the coin results in eitherCOIN_HEADS
orCOIN_TAILS
. As a result of the functionTable::work()
, the philosopher makes enough soup to fill a bowl. Similarly, the result of the functionTable::eat()
is that a philosopher picks a bowl and consumes all the soup in it. Note that there is no correspondence between bowls and philosophers, i.e. a philosopher will eat from any available bowl that contains soup, and will fill any available empty bowl. If all the bowls are full,work()
blocks until at least one is empty; if all of the bowls are empty,eat()
blocks until at least one is full.Think()
just yields the processor or sleeps.
Working with threads |
Before you begin the assignment, read Coding Standards for Programming with Threads. You are required to follow these standards for this project. Because it is impossible to determine the correctness of a multithreaded programming via testing, grading on this project will primarily be based on reading your code not by running tests. Your code must be clear and concise. If your code is not easy to understand, then your grade will be poor, even if the program seems to work. In the real world, unclear multi-threaded code is extremely dangerous -- even if it "works" when you write it, how will the programmer who comes after you debug it, maintain it, or add new features? Feel free to sit down with the TA or instructor during office hours for code inspections before you turn in your project.
You should also look at these hints for avoiding common pitfalls.
To simplify your task, we (really, Mike Dahlin) developed a simple thread package on top of the standard pthread package for your use. The idea is to shield you from the irrelevant detail that inevitably is part of dealing with pthreads. This way, you use the standard package but you also focus on the project at hand.
The files are sthread.h and sthread.cc. The package provides threads, mutex locks, and condition variables as well as some other utility functions that you may need (such as a wrapper on the library function to generate random numbers). This package is built on the Posix thread library. For more information, see the man pages for the library functions used in the sthread.cc code.
The libraries above and the template files below assume that you will do this lab in C++.
Exercise 1 |
The labT code lives in a different repository from the JOS code. To get the lab T code:tig% cd ~/CS372H tig% git clone http://www.cs.utexas.edu/~mwalfish/classes/s11-cs372h/labt.git Initialized empty Git repository in ......./CS372H/labt/.git/ got 212bdd4ac3e458c52a9147564a2c2826476c81ef walk 212bdd4ac3e458c52a9147564a2c2826476c81ef got cff94e941a2f5e767043e6b36e43534a2615842d got c8a805ff22b0e9a5897d7528f9cfe688e3211dfc ... got 4381a8371d1b3acff9c438ec3d7d02620571ff91 tig% cd labt tig%At this point, running make should produce the executable file main with no errors or warnings.
We have provided skeleton files for the
Table
object as well as aBarrier
object we will use to aid testing. TheTable
object is as described above -- it holds the bowls shared by the philosophers and has the public methodsTable::work()
(which increases the number of full bowls) andTable::eat()
(which reduces the number of full bowls). For debugging and testing, we also have given it the public methodTable::dbgCountFull()
(which returns the number of full bowls; this number should always be between0
andNBOWLS
.) The sharedBarrier
object is initialized with a count. A call toBarrier::waitUntilDone()
does not return until there have been at leastcount
calls toBarrier::done()
. Thus, a barrier allows a thread to wait forcount
other threads to complete their work.Complete the code for
Barrier
andTable
. Once you have done this, the two simple tests inmikesTest()
should succeed. Feel free to add any additional tests tomoreTests()
.
Exercise 2 |
Complete the philosophers simulation by creating a shared
Coin
object and writing therunPhilosophers()
method in main.cc.As noted above, the
Coin
has one public method,flip()
, which returnsHEADS
orTAILS
. The philosophers share a single coin (only one philosopher can flip the coin at a time.) Just to make it interesting, the coin is charmed: it has a "bias" towards "evening out" the working and the eating of the philosophers. In particular, the coin "remembers" the result of its last flip; if the last flip wasHEADS
, then there is a 3/4 chance that the current flip will returnTAILS
and if the last flip wasTAILS
, then there is a 3/4 chance that the current flip will returnHEADS
.
The
runPhilosophers()
method createsnPhilosophers
threads, each of which executes code corresponding to the pseudo-code above. Recall that the pseudocode for_start
(the "stub" for the "main" thread of a process) is roughly:
_start(...) { main(...); exit(); }So, you will need to make sure that the process continues to exist after spawning off the philosopher threads.
A philosopher thread should print "E(x)" when it eats (with x replaced by the number of full bowls left after the philosopher eats.) And, it should print "W(x)" when it works (with x replaced by the number of full bowls left after the philosopher works.) Notice that if you simply print out the value returned by
eat()
orwork()
, the output to the screen may not quite be what you expect. That's OK -- we could make the project more complex and require that you structure your code so that the output is more what you would expect. You don't need to do this for the lab -- just do the simple thing and print out the values returned byeat()
orwork()
. But, think about what is happening and how you would structure the code to avoid such issues.The philosophers hope that their new life is in harmony with nature and that they can sustain their existence indefinitely. Can they? (More to the point: is this system free from deadlock?) You don't need to turn in an answer to this question, but think about the deadlock properties of the system.
This completes the lab.
Turn-in procedure. |
When you are ready to hand in your lab code, create a file called slack.txt noting how many slack hours you have used both for this assignment and in total. (This is to help us agree on the number that you have used.)
Running make turnin should create a file containing all of the .h and .cc files you have modified or created and submit it to the grader via the CS turnin utility. If you submit multiple times, we will take the latest submission and count slack hours accordingly. If you are unsure about whether you submitted, use the options of turnin to help you: see man turnin and turnin --help.
Last updated: Tue Mar 01 20:24:44 -0600 2011 [validate xhtml]