Overview
Philosophers perform three core actions in a continuous cycle: eating, sleeping, and thinking. Each action is implemented as a function with careful mutex handling to ensure thread safety and prevent deadlocks.The Eat Action
ft_eat Function
Fork Acquisition Sequence
Handle Single Philosopher Case
time_to_die, then release the fork and return.All philosophers acquire their right fork first, then their left fork. This consistent ordering is critical for deadlock prevention.
Why Right Fork First?
The fork assignment ininit_philos creates an asymmetric pattern:
- Philosopher 1: left=fork[0], right=fork[n-1]
- Philosopher 2: left=fork[1], right=fork[0]
- Philosopher 3: left=fork[2], right=fork[1]
- Most philosophers take a “lower-indexed” fork first
- The first philosopher takes the “highest-indexed” fork first
- This breaks the circular wait condition that causes deadlock
Deadlock Prevention
Deadlock Prevention
If all philosophers tried to take their left fork first:
- All philosophers grab their left fork simultaneously
- All philosophers wait for their right fork (held by neighbor)
- Circular wait → deadlock
Eating Process
State Updates
Once both forks are acquired:- Set Eating Flag
- Increment Meal Counter
- Update Last Meal Time
- Sleep During Eating
- Clear Eating Flag
Fork Release
The
meal_counter increment is protected by lock_meal, but last_meal_time and eating are not locked during their updates in ft_eat. This is safe because:eatingis set to 1 before the monitor could detect itlast_meal_timeis updated whileeating=1, preventing false deaths- Only this philosopher writes these values
The Sleep Action
ft_sleep Function
Purpose
- Simulates the philosopher’s rest period after eating
- No mutexes needed (no shared state access)
- Duration specified by
time_to_sleepargument
The sleeping state gives other philosophers a chance to acquire forks. Without sleeping, a single philosopher might monopolize both their forks by immediately trying to eat again.
The Think Action
ft_think Function
Why No Delay?
Unlike eating and sleeping, thinking has no explicit duration:- The philosopher immediately tries to acquire forks after thinking
- The “thinking time” is implicitly the time spent waiting for forks
- This matches the problem description: philosophers think until hungry
Implicit Wait Time
Implicit Wait Time
When a philosopher calls This blocking wait is the “thinking” period—the philosopher thinks until both forks become available.
ft_think and then ft_eat, they will block on:The print_message Function
All actions useprint_message to output state changes:
Output Format
Thread Safety
- Serialized Output
- Death Check
lock_write mutex ensures that output from multiple philosophers doesn’t interleave, preventing garbled messages.print_message calls dead_loop, which locks lock_dead. This means two mutexes may be held simultaneously (lock_write and lock_dead). This is safe because the order is always consistent: lock_write first, then lock_dead (inside dead_loop).Timing Utilities
Actions rely on timing functions fromtime_utils.c:
get_current_time
Returns the current time in milliseconds (typically since epoch or simulation start).ft_usleep
A more precise sleep function:- Takes duration in milliseconds
- Uses
usleepor busy-waiting for accuracy - Critical for meeting tight timing requirements
Standard
usleep can be imprecise. The implementation may use a combination of sleep and busy-waiting to achieve accurate delays for time_to_eat and time_to_sleep.Action Cycle Summary
dead_loop returns true and the philosopher thread exits.