Skip to main content

What is Philosophers?

Philosophers is a C-based implementation of the classic Dining Philosophers Problem, a fundamental synchronization challenge in concurrent programming. This project demonstrates advanced concepts in multi-threading, mutual exclusion, and deadlock prevention using POSIX threads (pthread) and mutexes.
This project is part of the 42 curriculum and serves as a practical introduction to concurrent programming and resource synchronization challenges.

Why Does It Exist?

The Dining Philosophers Problem is one of the most famous problems in computer science, originally formulated by Edsger Dijkstra in 1965. This implementation exists to:
  • Teach Concurrency: Provide hands-on experience with multi-threaded programming
  • Demonstrate Synchronization: Show practical solutions to resource sharing and deadlock prevention
  • Explore Race Conditions: Illustrate how to safely manage shared state between threads
  • Practice C Programming: Apply low-level systems programming skills in a challenging context

What Problem Does It Solve?

The project tackles several critical challenges in concurrent systems:

Resource Contention

Multiple philosophers (threads) compete for limited resources (forks/mutexes) without causing deadlock

Starvation Prevention

Ensures no philosopher starves by monitoring meal times and preventing indefinite resource denial

Race Condition Management

Uses mutexes to protect shared state and prevent data races between concurrent threads

Timing Precision

Implements precise time tracking to monitor philosopher states and enforce timing constraints

Key Features

Thread-Based Architecture

The implementation creates one thread per philosopher, plus a monitor thread that oversees the entire simulation:
int ft_pthread_create(t_table *table, t_mtx *forks)
{
    pthread_t observer;
    int i;

    // Create monitor thread
    if (pthread_create(&observer, NULL, &monitor, table->philos) != 0)
        destroy_all("Thread creation error", table, forks);
    
    // Create philosopher threads
    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++;
    }
    // ... join threads
}

Mutex-Based Synchronization

Each fork is represented by a mutex, and the program uses multiple mutexes to protect shared resources:
  • Fork mutexes: One per fork (shared between adjacent philosophers)
  • Write mutex: Protects console output from race conditions
  • Dead mutex: Guards the death flag that stops simulation
  • Meal mutex: Protects meal counter updates
void ft_eat(t_philo *philo)
{
    pthread_mutex_lock(philo->right_fork);
    print_message("has taken a fork", philo, philo->id);
    
    pthread_mutex_lock(philo->left_fork);
    print_message("has taken a fork", philo, philo->id);
    
    philo->eating = 1;
    print_message("is eating", philo, philo->id);
    
    pthread_mutex_lock(philo->lock_meal);
    philo->meal_counter++;
    pthread_mutex_unlock(philo->lock_meal);
    
    philo->last_meal_time = get_current_time();
    ft_usleep(philo->time_to_eat);
    
    philo->eating = 0;
    pthread_mutex_unlock(philo->left_fork);
    pthread_mutex_unlock(philo->right_fork);
}

Deadlock Prevention

The program prevents deadlock through several mechanisms:
  • Even philosophers delay start: Creates asymmetry to break circular wait
  • Single philosopher special case: Handles edge case where only one fork exists
  • Consistent lock ordering: Each philosopher locks right fork then left fork
void *philo_routine(void *pointer)
{
    t_philo *philo;

    philo = (t_philo *)pointer;
    if (philo->id % 2 == 0)
        ft_usleep(1);  // Even philosophers wait 1ms
    
    while (!dead_loop(philo))
    {
        ft_eat(philo);
        ft_sleep(philo);
        ft_think(philo);
    }
    return (pointer);
}

Real-Time Monitoring

A dedicated monitor thread continuously checks for two termination conditions:
  1. Death condition: Any philosopher hasn’t eaten within time_to_die milliseconds
  2. Completion condition: All philosophers have eaten at least max_meals times (if specified)
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);
}

Precise Time Management

The program tracks microsecond-precision timestamps for:
  • Simulation start time
  • Last meal time for each philosopher
  • Action timestamps for logging
The implementation uses usleep() and gettimeofday() for timing. These functions may have platform-specific precision limitations.

Program Arguments

The program accepts 4 or 5 arguments:
./philo <number_of_philosophers> <time_to_die> <time_to_eat> <time_to_sleep> [number_of_times_each_philosopher_must_eat]
  • number_of_philosophers: Number of philosophers and forks (max 600)
  • time_to_die: Time in milliseconds before a philosopher dies from starvation
  • time_to_eat: Time in milliseconds a philosopher spends eating
  • time_to_sleep: Time in milliseconds a philosopher spends sleeping
  • number_of_times_each_philosopher_must_eat: Optional simulation stop condition
All time values are in milliseconds and must be positive integers. The program validates all inputs before starting the simulation.

Project Structure

The codebase is organized into focused modules:
  • main.c - Entry point and program flow
  • philo.h - Data structures and function declarations
  • init_data.c - Initialization of tables, philosophers, and mutexes
  • parse_input.c - Input validation and parsing
  • create_threads.c - Thread creation and philosopher routine
  • philo_actions.c - Philosopher actions (eat, sleep, think)
  • monitor.c - Death checking and simulation monitoring
  • time_utils.c - Time-related utility functions
  • utils.c - General utility functions

Next Steps

Problem Description

Learn about the classic dining philosophers problem in detail

Getting Started

Build and run the simulation on your system

Build docs developers (and LLMs) love