Development repo for 42cursus' Philosophers project
For further information about 42cursus and its projects, please check My Profile.
This project is about learning the basics of threading a process, learning how to make threads and discover the mutex.
Several philosophers are sitting at a round table doing one of three things: eating, thinking, or sleeping. While eating, they are not thinking or sleeping, while sleeping, they are not eating or thinking and of course, while thinking, they are not eating or sleeping. The philosophers sit at a circular table with a large bowl of spaghetti in the center. There are some forks on the table, it is assumed that a philosopher must eat with two forks, one for each hand. The philosophers must never be starving and every philosofer needs to eat. Philosophers don’t speak with each other and don’t know when another philosopher is about to die. Each time a philosopher has finished eating, he will drop his forks and start sleeping. When a philosopher is done sleeping, he will start thinking. The simulation stops when a philosopher dies.
What you should understand to succeed this project:
- Once or more philosophers are sitting at a round table either eating, either thinking, either sleeping, While they are eating, they do not think or sleep; while thinking they don't eat or sleep; and, of course, while sleeping, they do not eat or think.
- The philosophers sit at a circular table with a large bowl of spaghetti in the center.
- There are some forks on the table. Serving and eating spaghetti with a simple fork is very inconvenient, so the philosophers will eat with two forks, one for each hand.
- Each time a philosopher finishes eating, they will drop their forks and start sleeping. Once they have finished sleeping, they will start thinking. The simulation stops when a philosopher dies.
- Every philosopher needs to eat and they should never starve.
- Philosophers don't speak with each other.
- Philosophers don't know when another philosopher is about to die.
- No need to say the philosophers should avoid dying!
You have to write a program for the mandatory part and another on for the bonus part but they will have the same basic rules:
- Global variables are forbidden!
- 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]
number_of_philosophers
: is the number of philosophers and also the number of forks.time_to_die
: is in milliseconds, if a philosopher doesn't start eatingtime_to_die
milliseconds after starting their last meal or the beginning of the simulation, it dies.time_to_eat
: is in milliseconds and is the time it takes for a philosopher to eat. During that time they will need to keep the two forks.time_to_sleep
: is in milliseconds and is the time the philosopher will spend sleeping.number_of_times_each_philosopher_must_eat
: argument is optional, if all philosophers eat at leastnumber_of_times_each_philosopher_must_eat
the simulation will stop. If not specified, the simulation will stop only at the death of a philosopher.
- Each philosopher should be given a number from 1 to
number_of_philosophers
. - Philosopher number 1 is next to philosopher number
number_of_philosophers
. Any other philosopher with the numberN
is seated between philosopherN - 1
and philosopherN + 1
.
About the logs of the program:
- Any change of status of a philosopher must be written as follows (with
X
replaced with the philosopher number andtimestamp_in_ms
the current timestamp in milliseconds):
- timestamp_int_ms X has taken a fork
- timestamp_int_ms X has is eating
- timestamp_int_ms X has is sleeping
- timestamp_int_ms X has is thinking
- timestamp_int_ms X has died
- The status printed should not be scrambled or intertwined with another philosopher's status.
- You can't have more than 10ms between the death of a philosopher and when it will print its death.
- Again, philosophers should avoid dying!
The specific rules for the mandatory part are:
- Each philosopher should be a thread.
- One fork between each philosopher, therefore if they are multiple philosophers, there will be a fork at the right and the left of each philosopher.
- To avoid philosophers duplicating forks, you should protect the forks state with a mutex for each of them.
For The bonus part, the program takes the same arguments as before and should behave as explained in the General Instructions. The specific rules are:
- All the forks are in the middle of the table.
- They have no states in memory but the number of available forks is represented by a semaphore.
- Each philosopher should be a process and the main process should not be a philosopher.
name | prototype | description |
---|---|---|
usleep | int usleep(useconds_t usec); | shall cause the calling thread to be suspended from execution until either the number of realtime microseconds specified by the argument useconds has elapsed or a signal is delivered to the calling thread and its action is to invoke a signal-catching function or to terminate the process. The suspension time may be longer than requested due to the scheduling of other activity by the system. |
gettimeofday | int gettimeofday( struct timeval *, struct tzp * ); | fills two structures with details about the current time of day |
pthread_create | int pthread_create(pthread_t * restrict thread, const pthread_attr_t * restrict attr, void ( start_routine)(void *), void * restrict arg); | create a new thread |
pthread_detach | int pthread_detach(pthread_t thread); | marks the thread identified by thread as detached. When a detached thread terminates, its resources are automatically released back to the system without the need for another thread to join with the terminated thread. Attempting to detach an already detached thread results in unspecified behavior. |
pthread_join | int pthread_join(pthread_t thread, void ** retval); | join with a terminated thread. The function waits for the thread specified by thread to terminate. If that thread has already terminated, then pthread_join() returns immediately. The thread specified by thread must be joinable. |
pthread_mutex_init | int pthread_mutex_init(pthread_mutex_t * mutex, const pthread_mutexattr_t * attr); | initialises the mutex referenced by mutex with attributes specified by attr. If attr is NULL, the default mutex attributes are used; the effect is the same as passing the address of a default mutex attributes object. Upon successful initialisation, the state of the mutex becomes initialised and unlocked. |
pthread_mutex_destroy | int pthread_mutex_destroy(pthread_mutex_t * mutex); | destroys the mutex object referenced by mutex; the mutex object becomes, in effect, uninitialised. |
pthread_mutex_lock | int pthread_mutex_lock(pthread_mutex_t * mutex); | locks the object referenced by mutex. If the mutex is already locked, the calling thread blocks until the mutex becomes available. This operation returns with the mutex object referenced by mutex in the locked state with the calling thread as its owner |
pthread_mutex_unlock | int pthread_mutex_unlock(pthread_mutex_t * mutex); | releases the mutex object referenced by mutex. |
-
Mandatory:
man pthread
- PISOX Threads Programming
- Into to Parallel Computing
- POSIX thread (pthread) libraries
- Jacob Sober Playlist on Threads - Youtube
- Intro to Threads - Neso Academy
- Dinning Philosophers Problem - Wikipedia
- Multithreaded Programming Guide - Oracle
- Debugging a Multithreaded Program - Oracle
- Unix Threads in C - CodeVault Playlist
- https://www.youtube.com/watch?v=NbwbQQB7xNQ
- https://www.youtube.com/watch?v=vmSKp0PExRY
- Webianr: Mutexes & Semaphores Demystified
- CS360 Lecture notes -- A primer on mutexes and condition variables
- Using mutexes
-
Bonus:
-
Extra:
- Unix Threads in C -playlist by CodeVault
- Multi Threaded Programming Video by Arif Butt
- Synchronization among Threads Video by Arif Butt
- Multithreaded Programming
- Threads questions and answers by Joyce Macksuele
- Delay_ometer by Arseniy Shishaev
- Philosooher Visualizer by Nafuka
- Notion with study materials by Laís Arena
- ASCIImoji