Dining Philosophers simulation in C — a small concurrent program that demonstrates thread synchronization, timing, and resource-sharing constraints. This project follows the typical 42-school-style requirements.
The Dining Philosophers problem models N philosophers sitting around a circular table. Each philosopher alternates between thinking and eating. Between every pair of philosophers sits a single fork. To eat, a philosopher needs to pick up both the left and the right fork (two shared resources). The challenge is to coordinate access to forks so the system avoids deadlock and starvation while respecting timing constraints.
- Implement a correct, thread-based simulation using POSIX threads (
pthread). - Avoid deadlock and minimize starvation.
- Follow the specified time semantics (millisecond precision) for actions.
- Philosophers: represented by threads. Each thread cycles through thinking, trying to pick up forks, eating, and sleeping.
- Forks: represented by mutexes (one mutex per fork). A philosopher must lock the two mutexes corresponding to their left and right forks before eating.
- Monitor: a separate thread (or checking mechanism) verifies the timing constraints (e.g., whether a philosopher has died from starvation).
- Timing:
time_to_die,time_to_eat, andtime_to_sleepare interpreted in milliseconds. Use a reliablegettimeofdayorclock_gettimewrapper to measure elapsed time.
- Ordered locking: to reduce the chance of classical deadlock, philosophers may pick up the lower-numbered fork first (or use an asymmetric policy where odd philosophers pick left first, even pick right first).
- Single monitor thread: a central monitor periodically checks last-meal times and signals termination if a philosopher dies.
- Optional meal limit: when the optional
number_of_times_each_philosopher_must_eatis provided, the simulation ends when all philosophers have eaten that many times.
- The program uses pthreads and mutexes; compile with
-pthread(theMakefileprovided handles that). - Timing must be accurate to milliseconds.
- The simulation must gracefully terminate when a philosopher dies or when the optional meal count is reached.
Run:
makeThis produces the philo executable.
./philo number_of_philosophers time_to_die time_to_eat time_to_sleep [number_of_times_each_philosopher_must_eat]number_of_philosophers— integer > 0time_to_die,time_to_eat,time_to_sleep— milliseconds- optional
number_of_times_each_philosopher_must_eat— integer > 0
Examples:
./philo 5 800 200 200
./philo 4 410 200 200 7main.c: argument parsing and initialization.philo.h: shared types, structs for philosophers, and function prototypes.simulation.c: philosopher thread routines and main loop.monitor.c: monitor thread that checks for death conditions and termination.tools1.c,tools2.c,tools3.c,gc_tools.c: utility functions including timing helpers, printing/logging, and any simple garbage-collection helpers.
When modifying synchronization logic, prefer small, well-tested changes. Adding extra locks or changing the order of locking can introduce subtle deadlocks.
- Start with small numbers of philosophers and larger times to observe behavior.
- Use scenarios that commonly reveal deadlock: many philosophers with small
time_to_eatandtime_to_sleepvalues. - Add debug prints in
tools*.cto log when a mutex is locked/unlocked for deeper diagnosis (but remove or guard verbose logs for final submission).
- Millisecond timing depends on system scheduling and
usleepgranularity; on heavy-load systems timing may be coarser. - This implementation uses mutexes; alternative implementations may use semaphores or other synchronization primitives.
main.c— program entryphilo.h— headers and typessimulation.c,monitor.c— core logic and monitoringtools1.c,tools2.c,tools3.c,gc_tools.c— helpersMakefile— build rules
- Requires pthreads (POSIX threads).
- Tested on Linux.
If you'd like, I can also run make here and report the build output or add
short examples of debug commands to help reproduce issues.