A small project to learn the basics of threading a process and resource sharing. On the basis of the "dining philosophers problem", originally formulated by Edsger Dijkstra. Inspired by the "42 Coding School" exercise "philosophers" (February 2022).
memset, printf, malloc, free, write, usleep, gettimeofday, pthread_create, pthread_detach, pthread_join, pthread_mutex_init, pthread_mutex_destroy, pthread_mutex_lock, pthread_mutex_unlock
The aim of the exercise is to write a program which does a simulation of the dining philosophers. The situation is as follows: One or more philosophers sit around a round table. Each philosopher has their own place at the table with a plate and a fork on its right-hand side.
The dish served is a very difficult kind of spaghetti, that has to be eaten with two forks. To achieve this the philosophers have to also use the fork of their neighbor. As a consequence philosophers sitting next to each other can not eat simultaneously.
The philosophers are alternatively eating, sleeping or thinking. When a philosopher has finished eating, they put the forks back on the table and start sleeping. Once awake, they start thinking. The simulation stops when a philosopher dies of starvation. This should be avoided. However philosophers don’t speak with each other and don’t know if another philosopher is about to die.
Illustration[^1] of the "dining philosophers problem": [^1]: From Wikipedia: https://en.wikipedia.org/wiki/Dining_philosophers_problem
The program should take the following arguments:
number_of_philosophers time_to_die time_to_eat time_to_sleep [number_of_times_each_philosopher_must_eat]
Next: Prerequisites Previous: Introduction [Contents]
I started to learn and experiment with threads watching the videos linked in the resources. I learned how to create threads, how to pass arguments to threads and how to use mutex locks.
The following quote is a very good and funny metaphoric exlanation on threads, resourece sharing and mutex protection:
When I am having a big heated discussion at work, I use a rubber chicken which I keep in my desk for just such occasions. The person holding the chicken is the only person who is allowed to talk. If you don't hold the chicken you cannot speak. You can only indicate that you want the chicken and wait until you get it before you speak. Once you have finished speaking, you can hand the chicken back to the moderator who will hand it to the next person to speak. This ensures that people do not speak over each other, and also have their own space to talk. Replace Chicken with Mutex and person with thread and you basically have the concept of a mutex. Of course, there is no such thing as a rubber mutex. Only rubber chicken. My cats once had a rubber mouse, but they ate it. Of course, before you use the rubber chicken, you need to ask yourself whether you actually need 5 people in one room and would it not just be easier with one person in the room on their own doing all the work. Actually, this is just extending the analogy, but you get the idea.[^2] [^2]: From Stack Overflow: https://stackoverflow.com/a/34558
Basically every philosopher (thread) is going to executing an infinie while loop with the philosopher's routine (eat-sleep-think-repeat, as described above), until one of them dies of starvation or all of them have eaten the number of required meals. As they should not communicate with each other, a external control instance is needed.
The tricky part of this project comes with the parallelism and the use of shared resources (e.g. the forks). To prevent data races the shared resources have to be protected using _mutexlocks. This has to be coordinated in a way that no deadlocks can occure. The -fsanitize=thread
flag is very useful to identify and find potential problems.
I approached this project in three main steps. First I created the "frame" that sets up the simulation. After this I worked out the routine and ended with the external control instance.
For the frame my goal was to code the program up to the point were it would recieve the user input from the command line, do the input error checking and launch the right number of threads (given by the user input) that will run the routine. For now the routine should only print what I call the philosopher_id (the number of the philosopher from 1 to n). Once I managed that, I went on with the routine.
I changed from printing the philosopher_id to printing the required logs, as stated above. To have an easyer start, I first developed the routine without using any mutex_locks, ignoring the fact that a fork can not be used by two philosophers at the same time. When I got this going, I protected the forks of simultaneous use by locking and unlocking the corresponding mutex_locks.
I developed a control function that constantly loops through all the philosophers and checks if a one of them has to die or they ate all their meals. If I detect one of the mentioned cases, I set a flag (which is checked by the philosophers constantly) to indicate that the simulation has to end. I lock the log printing so that no further logs get printed after the notification of the death. The flag and all other shared variables have to be protected with mutex_locks.
Next: How to launch Previous: Approach [Contents]
sudo apt-get install gcc
)sudo apt-get install make
)sudo apt-get install libpthread-stubs0-dev
)Next: Example Previous: Prerequisites [Contents]
Compile the program via the Makefile by using make
in the root directory of the repository.
Run it like this:
./philo 4 800 200 200
For a description of the arguments see: Arguments.
Next: Resources Previous: How to launch [Contents]
Terminal output for the following examples:
Example 1 | Example 2 | Example 3 |
---|---|---|
Next: Notes Previous: Example [Contents]
Code Vault: Playlist - Unix Threads in C Video 1 (Short introduction to threads) - Video 7 (How to pass arguments to threads in C)
Jacob Sorber: Playlist - Programming with Threads Video 1 (How to create and join threads in C) - Video 3 (Safety and Speed Issues with Threads)
Previous: Resources [Contents]
Depending on the performance of the Computer philosphers may die (faster) on slower computers, but live on faster ones. Especially in extreme cases for example high number of philosophers (>200) or short times to eat and/or sleep (<60 ms).