This project is an implementation of the classic Dining Philosophers problem using C and POSIX threads. The goal is to simulate philosophers sitting at a table, alternating between thinking, eating, and sleeping, while sharing forks (resources) and avoiding deadlocks and starvation. This project is part of the 42 curriculum and aims to deepen understanding of concurrency, synchronization, and thread safety.
- Multithreading with pthreads: Creating, joining, and managing threads in C.
- Mutexes and synchronization: Using
pthread_mutex_tto protect shared resources and prevent race conditions. - Deadlock avoidance: Implementing strategies to avoid deadlocks by controlling the order of fork acquisition.
- Precise timing: Using
gettimeofdayand custom sleep functions for accurate simulation timing. - Resource cleanup: Properly destroying mutexes and freeing memory to avoid leaks.
- Error handling: Validating input and handling errors gracefully.
- Code analysis tools: Using Valgrind and AddressSanitizer (
-fsanitize=address) to check for memory leaks, undefined behavior, and potential deadlocks (including pthread deadlocks).
In the Philosophers directory, run:
makeThis will produce the philo executable.
To check for memory leaks and undefined behavior, use:
- Valgrind:
valgrind --leak-check=full ./philo 5 800 200 200
- AddressSanitizer (fsanitize):
Add
-fsanitize=address -gto yourCFLAGSin the Makefile, or run:(You may need to modify your Makefile to support themake SANITIZE=1 ./philo 5 800 200 200
SANITIZEvariable.)
Run the program with the following arguments:
./philo number_of_philosophers time_to_die time_to_eat time_to_sleep [number_of_times_each_philosopher_must_eat]number_of_philosophers(int): Number of philosophers (1-200)time_to_die(ms): Time a philosopher can go without eating before dyingtime_to_eat(ms): Time a philosopher spends eatingtime_to_sleep(ms): Time a philosopher spends sleepingnumber_of_times_each_philosopher_must_eat(optional, int): If specified, simulation ends when each philosopher has eaten at least this many times
Example:
./philo 5 800 200 200Philosophers/
├── Makefile
├── philo
├── include/
│ └── philo.h
└── srcs/
├── action.c
├── ft_atoi.c
├── ft_isdigit.c
├── init.c
├── monitor.c
├── philo.c
└── utils.c
philo.h: Main header file with all structures and function prototypes.action.c: Philosopher actions (eat, sleep, think).monitor.c: Monitoring thread for death and meal count.init.c: Initialization of data structures and mutexes.utils.c: Utility functions (timing, printing, cleanup).philo.c: Main function, argument parsing, thread creation.
- Mutex per fork: Each fork is a mutex, ensuring only one philosopher can hold it at a time.
- Consistent fork acquisition order: Philosophers always pick up the lower-numbered fork first to avoid deadlocks.
- Monitor thread: A separate thread checks for philosopher death and meal completion, ensuring timely detection.
- Thread-safe printing: Output is protected by a mutex to avoid garbled logs.
- Custom sleep: Accurate timing is achieved with a custom sleep function to avoid timing drift.
- Checks for correct number and validity of arguments.
- Handles memory allocation and mutex initialization failures.
- Prints clear error messages for invalid input.
- Initialization: Parses arguments, initializes philosophers, forks (mutexes), and shared data.
- Thread Creation: Each philosopher runs in its own thread, performing actions in a loop.
- Fork Handling: Philosophers pick up forks (mutexes) in a specific order to avoid deadlocks.
- Monitoring: A separate thread monitors for death (time since last meal exceeds
time_to_die) and for all philosophers eating enough times. - Cleanup: All resources (threads, mutexes, memory) are properly released at the end.
- Dining Philosophers Problem - Wikipedia
- 42 PDF subject (project requirements)
For more details, see the source code in the Philosophers directory.