CS 3733 Operating Systems: Assignment 4

In this assignment, we will practice threads and synchronizations. This assignment has several parts, with 90 points in total.
This project will implement the "Dinging Philosophers" problem with multithreading. The "Dining Philosophers" problem is invented by E. W. Dijkstra. Imagine that five philosophers who spend their lives just thinking and easting, without anything else. They will sit on a circular table with five chairs. The table has a big plate of spaghetti. However, there are only five chopsticks available, as shown in the figure.
Each philosopher thinks at first. When he gets hungry, he picks up the two chopsticks close to him, on the left side and the right side of this philosopher. If a philosopher can pick up both chopsticks, he eats for a while. After a philosopher finishes his eating, he puts down the chopsticks and leaves.

Now we will write a multithreaded program to simulate the behavior of phylosophers. The whole project will be divided to 3 parts. In total, this project will be 90 points in total.

Part 1 (30 points)
Create a main program that takes a single command line parameter, the number of threads ("nthreads") that is going to be created. You will finish this part in several steps:
(1) Intepret the parameter in order to get "nthreads".
(2) Print your name and the "nthreads" in the same statement, e.g. "Tongping Liu, the input is 5 threads".
(3) The main function will invoke a function
    void creatPhilosophers(int threadindex).
The "nthreads" is actually the number of threads that you are going to create in your program. For each thread, creatPhilosophers() will pass the index of each thread to the thread function "PhilosopherThread". For instance, if you are creating 5 threads, and the thread index will be from 0 to 4.
(4) After the creation, every PhilosopherThread simply prints a sentence like "This is philosopher X", where X is the actual index passed by the creatPhilosophers(). After do this, the PhilosopherThreads just return NULL;
(5) After the creation, the main thread will wait for the finish of all philosopher threads using pthread_join() API. After all threads have been joined successfully, the function will print a sentence like "N threads have been joined successfully now".
For the submission, you should output all of these results into part1-ouput.txt (but not status.txt); Please test on two cases, nthreads is 5 and 20.
Please explain how you utilize the pthread_join in "status.txt". The corresponding code will be in "part1.c" under the source directory.

Part2: using multiple mutexes. (40 points)
Now you are going to solve the philosopher problem with pthread's mutex APIs.
Each philosopher is in a "thinking"-"picking up chopsticks"-"eating"-"putting down chopsticks" cycle as shown below.
Thus, you can creat four different functions to implement these four steps correspondingly. These functions will have the deterministic signatures as follows.
    void thinking();
    void pickUpChopsticks(int threadIndex);
    void eating();
    void putDownChopsticks(int threadIndex);

The "pick up chopsticks" part is the key point of this project. Each chopstick is shared by two philosophers and hence a shared resource. We certainly do not want a philosopher pick up a chopstick that has already been picked up by his neighbor, which will be a race condition. To address this problem, we may implement each chopstick as a mutex lock. Each philosopher, before he can eat, he should lock his left chopstick and lock his right chopstick. If the acquisitions of both locks are successful, this philosopher now owns two locks (hence two chopsticks), and can eat. After finishes easting, this philosopher invokes the function putDownChopsticks() to release both chopsticks, and exit the current thread!
Both "eating" and "thinking" functions can be easily simulated by invoking a usleep() API inside. Please use "man usleep" to find out the signature of usleep() API. However, we can not utilize a determined number in the invocation of usleep().
One method is to utilize a random number between 1 to 500. You could utilize random() API to get the number of the random value, which could be initilized use srandom() to seed this random generator. The definition of these APIs can be checked using "man random".
However, this simple solution may have two problems. One is starvation, another is the deadlock problem. See the link for more descriptions of these two problems. Since each philosopher only eats once before his exit in part2, then starvation is not an issue here. But deadlocks will be an issue. Deadlocks occur when every philosopher sits down and picks up his left chopstick at the same time? In this case, all chopsticks are locked and none of the philosophers can successfully lock his right chopstick. As a result, we have a circular waiting (i.e., every philosopher waits for his right chopstick that is currently being locked by his right neighbor), and hence a deadlock occurs.
Note1: The corresponding code for this part will be in "part2.c" under the source directory. You will test your program again using different number of threads, such as 2 threads or 20 threads. Trying to output the sequence of eating to the part2-ouput.txt (but not status.txt); That is, you need to print out "philosopher ith is eating" in your eating() funtion, where ith should be replaced with the actual index of each thread.
Note2:You will run your program 1000 times, where you can use a scriptto perform the tests. Then report whether you met the deadlock problem or not in the status.txt. Also, please also think about when it is easier to see the deadlock, with the small or the large number of threads? Also, you need to put this reason into the "status.txt" file as well. If you are not sure about this, you could use your program to verify this.

Part3: solve the deadlock problem with one lock and one conditional variable(20 bonus points)
In this part, you can solve the problem using only one mutex object. If there is only one mutex in the system, then there is no deadlock problem any more.
For part3, you will need to enforce the order of eating. The 0th philosopher should eat at first, then 1th philosopher, and so on.
The idea is to utilize a conditional variable and an index that indicates which philosopher should be the next one to eat. Initially, before creating threads, we could set the index to 0 in order for the first one to eat at first.
When every thread finishs their thinking and before they grab the chopsticks, they will check whether it is their turn to eat or not. If yes, then they will perform the eating. Otherwise, they will wait on the shared conditional variable. The thread that release the lock will wake up all threads waiting on the conditional variable. But only one of them can move forward based on the determined order, whether the index of the thread is the next one or not. If not, then those threads will have to wait again.

Note: The implementation will be in "part3.c" under the source directory. Similarly, let's use 2 threads to test your results. Run this program for 1000 times again and confirms that there is no deadlock anymore.Please include the printing of the eating order to part3-ouput.txt (but not status.txt); Please explain how you design your conditional variables to enforce the order of eating. You can copy the key part of your code into the "status.txt" file to assist the explaination.

Submission guideline

You will create a directory---assignment4-abc123 directory---at first, where abc123 should be replaced with your abcid. In this directory, you will further have source directory and status.txt file.
"source" directory: mandatory
This directory should include all of your source code (*.c, *.h) and Makefile. Without the soure code, you can only get 50% points of your earned at most.

status.txt and partX-ouput.txt: manadatory
You should include a status report in the file status.txt. Here, the status report in status.txt should include whether you have complete different parts successfully. Please also give brief description of what is working and what progress was made on the part that is not working in status.txt, as further described before. But the partX-output.txt will include the detailed output. There will be a severe penalty (50% off) if the "status.txt" is not aligned with your source code. If there is no "status.txt", you won't get any points.

Please compress the directory into a zipped file, named as "assignment4-abc123.zip". Here, abc123 should be replaced with your abcid.

Based on the materials from this link.