Skip to main content
The dining philosophers simulation has specific conditions that cause it to terminate. This page explains all exit conditions, the monitoring mechanism, and cleanup procedures.

Overview

The simulation runs continuously until one of two conditions is met:
  1. Death Condition: A philosopher dies from starvation
  2. Completion Condition: All philosophers have eaten the required number of meals (if specified)
Both conditions are monitored by a dedicated monitor thread that runs concurrently with the philosopher threads.

The Monitor Thread

The monitor thread is created before any philosopher threads and continuously checks for termination conditions.
void *monitor(void *pointer)
{
    t_philo *philos;

    philos = (t_philo *)pointer;
    while (1)
        if (check_if_dead(philos) == 1 || check_if_all_ate(philos) == 1)
            break ;
    return (pointer);
}
Location: monitor.c:83-92 Key characteristics:
  • Runs in an infinite loop
  • Checks both conditions on every iteration
  • Terminates immediately when either condition is met
  • Sets the dead_flag before returning

Death Condition

How Philosopher Death is Detected

A philosopher is considered dead when they haven’t eaten within the time_to_die limit.
int philosopher_dead(t_philo *philo, size_t time_to_die)
{
    pthread_mutex_lock(philo->lock_meal);
    if (get_current_time() - philo->last_meal_time >= time_to_die
        && philo->eating == 0)
        return (pthread_mutex_unlock(philo->lock_meal), 1);
    pthread_mutex_unlock(philo->lock_meal);
    return (0);
}
Location: monitor.c:26-34

Death Detection Logic

The monitor checks all philosophers in sequence:
int check_if_dead(t_philo *philos)
{
    int i;

    i = 0;
    while (i < philos[0].nbr_philo)
    {
        if (philosopher_dead(&philos[i], philos[i].time_to_die))
        {
            print_message("died", &philos[i], philos[i].id);
            pthread_mutex_lock(philos[0].lock_dead);
            *philos->dead = 1;
            pthread_mutex_unlock(philos[0].lock_dead);
            return (1);
        }
        i++;
    }
    return (0);
}
Location: monitor.c:36-54

Death Condition Requirements

For a philosopher to be declared dead, both conditions must be true:
  1. (current_time - last_meal_time) >= time_to_die
  2. eating == 0 (not currently eating)
The second condition prevents a false death detection when a philosopher acquires forks but hasn’t updated last_meal_time yet.

Thread-Safe Death Flag

All philosopher threads check the death flag before each action:
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);
}
Location: create_threads.c:15-22 This ensures:
  • No race conditions when accessing the shared dead_flag
  • Philosophers stop immediately when any termination condition is met
  • No philosopher prints actions after death

Completion Condition

Checking Meal Count

If the optional 5th argument is provided, all philosophers must eat at least that many times.
int check_if_all_ate(t_philo *philos)
{
    int i;
    int finished_eating;

    i = 0;
    finished_eating = 0;
    if (philos[0].max_meal == -1)
        return (0);
    while (i < philos[0].nbr_philo)
    {
        pthread_mutex_lock(philos[i].lock_meal);
        if (philos[i].meal_counter >= philos[i].max_meal)
            finished_eating++;
        pthread_mutex_unlock(philos[i].lock_meal);
        i++;
    }
    if (finished_eating == philos[0].nbr_philo)
    {
        pthread_mutex_lock(philos[0].lock_dead);
        *philos->dead = 1;
        pthread_mutex_unlock(philos[0].lock_dead);
        return (1);
    }
    return (0);
}
Location: monitor.c:56-81

Completion Condition Requirements

1

Optional Parameter

The 5th command-line argument (number_of_times_each_philosopher_must_eat) must be provided. If not provided, max_meal is set to -1 and this condition is never checked.
2

All Must Finish

Every philosopher must have meal_counter >= max_meal. If even one philosopher hasn’t finished, the simulation continues.
3

No Death Message

Unlike the death condition, completion is silent. The simulation simply stops without printing any message.

Philosopher Thread Termination

Each philosopher thread runs a loop that checks the dead flag:
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);
}
Location: create_threads.c:24-38 When dead_loop() returns 1, the philosopher thread exits its loop and returns, allowing pthread_join to proceed.

Cleanup and Resource Destruction

After all threads complete, resources are cleaned up:
void destroy_all(char *str, t_table *table, t_mtx *forks)
{
    int i;

    i = 0;
    if (str)
    {
        write(2, str, ft_strlen(str));
        write(2, "\n", 1);
    }
    pthread_mutex_destroy(&table->lock_write);
    pthread_mutex_destroy(&table->lock_meal);
    pthread_mutex_destroy(&table->lock_dead);
    while (i < table->philos[0].nbr_philo)
    {
        pthread_mutex_destroy(&forks[i]);
        i++;
    }
}
Location: utils.c:46-64

Cleanup Order

1

Thread Joining

First, the main thread waits for the monitor thread and all philosopher threads to complete via pthread_join.
2

Mutex Destruction

Then destroy_all destroys all mutexes:
  • Table mutexes (lock_write, lock_meal, lock_dead)
  • All fork mutexes
3

Program Exit

The program returns from main() with exit code 0.

Edge Cases

Single Philosopher

When there is only one philosopher, they will always die:
void ft_eat(t_philo *philo)
{
    pthread_mutex_lock(philo->right_fork);
    print_message("has taken a fork", philo, philo->id);
    if (philo->nbr_philo == 1)
    {
        ft_usleep(philo->time_to_die);
        pthread_mutex_unlock(philo->right_fork);
        return ;
    }
    // ... rest of eating logic
}
Location: philo_actions.c:28-35
With only one philosopher:
  • They pick up the only fork
  • They cannot pick up a second fork (none available)
  • They wait for time_to_die milliseconds
  • The monitor detects death
  • Simulation terminates with death message

Immediate Death Scenarios

Zero time_to_die

If time_to_die is 0, philosophers die immediately as (current_time - last_meal_time) >= 0 is always true.

Impossible timing

If time_to_die < time_to_eat, philosophers will die while eating since they can’t complete a meal before the death timer expires.

Too many philosophers

With many philosophers and short time_to_die, contention for forks may cause starvation before anyone finishes eating.

Max meals is 0

If max_meal is 0, all philosophers have already “finished” and the simulation exits immediately (completion condition met).

Thread Synchronization During Exit

Monitor-First Termination

The main thread joins the monitor thread before joining philosopher threads:
if (pthread_join(observer, NULL) != 0)
    destroy_all("Thread join error", table, forks);
while (i < table->philos[0].nbr_philo)
{
    if (pthread_join(table->philos[i].thread, NULL) != 0)
        destroy_all("Thread join error", table, forks);
    i++;
}
Location: create_threads.c:56-63 This ensures:
  • The termination condition has been detected and dead_flag is set
  • Philosopher threads will exit quickly after the monitor completes
  • No philosopher thread outlives the monitor

Silent Message Suppression

After dead_flag is set, philosophers stop printing:
void print_message(char *str, t_philo *philo, int id)
{
    size_t time;

    pthread_mutex_lock(philo->lock_write);
    time = get_current_time() - philo->start_time;
    if (!dead_loop(philo))
        printf("%zu %d %s\n", time, id, str);
    pthread_mutex_unlock(philo->lock_write);
}
Location: monitor.c:15-24 The !dead_loop(philo) check prevents:
  • Messages after a philosopher dies
  • Messages after all philosophers finish eating
  • Interleaved output during shutdown

Summary Table

ConditionTriggerMessageFlag SetExit Code
Death(time - last_meal) >= time_to_die"<timestamp> <id> died"Yes0
CompletionAll meal_counter >= max_mealNoneYes0
Thread Errorpthread_create or pthread_join failsWrites error to stderrN/AExits in destroy_all
Single PhiloOnly 1 philosopher"<timestamp> 1 died"Yes0
Both normal exit conditions (death and completion) result in a clean exit with code 0. Only thread creation/join errors cause abnormal termination.

Build docs developers (and LLMs) love