Skip to main content

Overview

Each philosopher thread executes the philo_routine function, which implements the classic eat-sleep-think cycle. The routine continues until the simulation ends (either by a death or all philosophers finishing their meals).

The philo_routine Function

void    *philo_routine(void *pointer)
{
    t_philo *philo;

    philo = (t_philo *)pointer;
    if (philo->id % 2 == 0)
        ft_usleep(1);
    while (!dead_loop(philo))
    {
        ft_eat(philo);
        ft_sleep(philo);
        ft_think(philo);
    }
    return (pointer);
}
Source: create_threads.c:24

Execution Flow

1. Thread Initialization

philo = (t_philo *)pointer;
The function receives a void pointer to the philosopher’s data structure and casts it to the appropriate type.

2. Even Philosopher Delay

if (philo->id % 2 == 0)
    ft_usleep(1);
Even-numbered philosophers are delayed by 1 millisecond before entering the main loop. This staggered start helps prevent all philosophers from attempting to grab forks simultaneously, reducing initial deadlock risk.
Why This Works:
  • Odd philosophers (1, 3, 5…) start immediately
  • Even philosophers (2, 4, 6…) wait 1ms
  • This creates an alternating pattern where philosophers are slightly out of phase
  • Reduces contention for forks at simulation start

3. Main Execution Loop

while (!dead_loop(philo))
{
    ft_eat(philo);
    ft_sleep(philo);
    ft_think(philo);
}
The philosopher repeatedly cycles through three actions until dead_loop returns true (simulation ended).
1

Eat

Acquire both forks, eat for time_to_eat milliseconds, then release forks.
2

Sleep

Sleep for time_to_sleep milliseconds.
3

Think

Print thinking status (no delay).

Death Detection: dead_loop

The dead_loop function is called at every loop iteration to check if the simulation has ended:
int dead_loop(t_philo *philo)
{
    pthread_mutex_lock(philo->lock_dead);
    if (*philo->dead == 1)
        return (pthread_mutex_unlock(philo->lock_dead), 1);
    pthread_mutex_unlock(philo->lock_dead);
    return (0);
}
Source: create_threads.c:15

How It Works

pthread_mutex_lock(philo->lock_dead);
// *philo->dead == 0
pthread_mutex_unlock(philo->lock_dead);
return (0);  // Continue loop
When the dead flag is 0, the function unlocks the mutex and returns 0, allowing the loop to continue.
The comma operator in the return statement ensures the mutex is unlocked before returning: (unlock, 1) executes unlock first, then returns 1.

Thread Safety

  • The lock_dead mutex protects concurrent access to the shared dead flag
  • All philosophers check the same flag pointer: *philo->dead
  • The monitor thread sets this flag when:
    • A philosopher dies (exceeds time_to_die without eating)
    • All philosophers complete their required meals

Timing and Flow Control

Action Sequence

Philosopher Thread Lifecycle:

  Start
    |
    v
[Even ID?] --Yes--> [Wait 1ms]
    |
    No
    |
    v
┌─────────────┐
│ dead_loop() │<────────┐
└──────┬──────┘         │
       │                │
     False              │
       │                │
       v                │
   [ft_eat()]           │
       │                │
       v                │
  [ft_sleep()]          │
       │                │
       v                │
  [ft_think()]          │
       │                │
       └────────────────┘

     True

       v
    [Exit]

No Delay in Thinking

Notice that ft_think does not include a sleep call:
void    ft_think(t_philo *philo)
{
    print_message("is thinking", philo, philo->id);
}
Source: philo_actions.c:15 The philosopher immediately proceeds to attempt eating again after thinking. The “thinking time” is implicitly the time spent waiting to acquire forks.

Preventing Race Conditions

The dead_loop function always locks lock_dead before reading the flag, preventing race conditions between:
  • Philosopher threads checking the flag
  • Monitor thread setting the flag
Checking dead_loop at the start of each iteration ensures:
  • Clean exits when simulation ends
  • No philosophers continue acting after death is detected
  • Graceful thread termination

Thread Creation

Philosopher threads are created in ft_pthread_create:
i = 0;
while (i < table->philos[0].nbr_philo)
{
    if (pthread_create(&table->philos[i].thread, NULL, &philo_routine,
            &table->philos[i]) != 0)
        destroy_all("Thread creation error", table, forks);
    i++;
}
Source: create_threads.c:48 Each philosopher gets:
  • A unique thread handle stored in philos[i].thread
  • A pointer to their own t_philo structure as the thread argument
  • The philo_routine function as their entry point
The monitor thread is created before philosopher threads, ensuring it’s ready to detect deaths from the very beginning of the simulation.

Build docs developers (and LLMs) love