My seventh project in 42. . . sometimes I wonder if this is the programming trenches ( ;´ - `;)
the infamous Dining Philosophers Problem — a classic in concurrent programming:
Five philosophers sit around a table. Each needs two forks to eat. But forks are shared between neighbors. They alternate between thinking, eating, and sleeping.
Your task is to simulate this scenario using multithreading, avoiding:
- 🔁 Deadlocks (e.g., everyone grabs one fork and waits forever)
- ⚔️ Race conditions (two threads accessing shared data unsafely)
- 😵 Starvation (a philosopher never gets to eat)
Requirement | Description |
---|---|
Threads | One thread per philosopher |
Shared Resources | Forks = Mutexes |
Timing | Precise delays for eating, sleeping, dying |
Input | CLI args: |
./philo number_of_philosophers time_to_die time_to_eat time_to_sleep [meals_required] |
|
Output | Timestamped logs of actions |
Death | Detect when a philosopher hasn't eaten in time |
Concept | Purpose |
---|---|
pthread_create |
Create threads (one per philosopher) |
pthread_mutex_t |
Lock/unlock forks (shared resources) |
gettimeofday() |
Track precise time for actions |
usleep() |
Sleep during eating and sleeping |
pthread_join / detach |
Manage thread lifecycle |
malloc / free |
Dynamic memory for threads, forks, etc. |
philosophers/
├── main.c
├── init.c // Data + thread setup
├── philo.c // Philosopher behavior
├── monitor.c // Death checker
├── utils.c // Time & print helpers
├── Makefile
├── philo.h // Structs & headers
typedef struct s_philo {
int id;
int meals_eaten;
long long last_meal;
pthread_mutex_t *left_fork;
pthread_mutex_t *right_fork;
pthread_t thread;
struct s_data *data;
} t_philo;
typedef struct s_data {
int num_philos;
long long time_to_die;
long long time_to_eat;
long long time_to_sleep;
int must_eat;
int dead;
long long start_time;
pthread_mutex_t *forks;
pthread_mutex_t print_mutex;
pthread_mutex_t death_mutex;
t_philo *philos;
} t_data;
Each philosopher thread loops:
- Think
- Lock left fork
- Lock right fork
- Eat
- Unlock both forks
- Sleep
- Repeat...
Meanwhile, a monitor thread checks:
- Did anyone exceed
time_to_die
? - Did all philosophers eat
must_eat
times?
Function | Use |
---|---|
pthread_mutex_init |
Initialize forks (mutexes) |
pthread_create |
Spawn philosopher threads |
pthread_mutex_lock |
Lock a fork |
gettimeofday() |
Get current timestamp in ms |
usleep() |
Sleep precisely |
pthread_join |
Wait for threads to finish |
pthread_mutex_destroy |
Clean up at end |
- Ensure all arguments are positive integers.
- Handle optional
must_eat
argument.
- Create
num_philos
mutexes for forks. - Allocate
t_philo
array and link left/right forks.
- Launch
philo_routine(void *arg)
for each philosopher.
-
While no one is dead:
- Lock left + right forks
- Print action → "is eating"
usleep(time_to_eat)
- Unlock forks
- Print → "is sleeping"
usleep(time_to_sleep)
- Print → "is thinking"
-
Constantly check:
current_time - last_meal >= time_to_die
- If all
meals_eaten >= must_eat
- Use
print_mutex
so logs don’t overlap. - Format:
[timestamp] [philo_id] is eating
- Destroy all mutexes
- Free malloc’d memory
- 1 philosopher (should die alone 🪦)
- 5 philosophers, default case
- Even/odd numbers
- Long sleeps to test starvation
- Meals count limit
- Valgrind = 0 leaks ✅
❌ Problem | ✅ Solution |
---|---|
Deadlocks | Use odd/even fork order strategy |
Race conditions | Protect shared values with mutexes |
Imprecise sleeping | Use gettimeofday + custom smart_sleep() |
Unlocked mutexes | Always unlock after eating, even on error |
This project will:
- Warp your brain with mutexes.
- Force you to think in threads.
- Test your patience with timing.
- Teach you how to build robust concurrency logic from scratch.
Sometimes I wonder if the true philosopher is me — sitting at my desk, starving, debugging mutex deadlocks at 3am.
May all your philosophers eat, and none die in vain. 🥄