All projects

completedC / POSIX Threads / Linux

Philosophers

A multithreaded C simulation of the Dining Philosophers problem, using POSIX threads and mutexes to prevent deadlocks while enforcing strict timing constraints.

  • C
  • POSIX Threads
  • Linux
~/philosophers/philo
$ ./philo 1 800 200 200
0 1 has taken a fork
800 1 died
$ ./philo 4 410 200 200 1
0 1 has taken a fork
0 1 has taken a fork
0 1 is eating
0 3 has taken a fork
0 3 has taken a fork
0 3 is eating
200 4 has taken a fork
200 4 has taken a fork
200 4 is eating
200 2 has taken a fork
200 2 has taken a fork
200 2 is eating
Real output from building and running the public repo.

Overview

An implementation of the classic Dining Philosophers concurrency problem in C using POSIX threads, where each philosopher runs as its own thread alternating between thinking, eating, and sleeping while sharing a fixed number of forks with its neighbors. The simulation enforces strict timing rules, a philosopher who doesn't eat before a configurable time-to-die limit is detected as dead, so correctness depends as much on precise timing as on avoiding deadlocks and race conditions.

Architecture

Each philosopher runs on its own pthread executing a shared routine that cycles through thinking, taking two fork mutexes, eating, releasing them, and sleeping. A separate monitor thread runs concurrently, checking every philosopher's last-meal timestamp against the time-to-die limit to detect starvation without interrupting the eating threads. A dedicated timing helper breaks sleep calls into short intervals so the monitor can still detect a death promptly instead of oversleeping past the deadline.

Key Features

  • Independent philosopher threads synchronized with per-fork mutexes to prevent two neighbors eating with the same fork at once
  • Dedicated monitor thread that detects philosopher death based on a configurable time-to-die limit
  • Optional meal-count limit so the simulation can end once every philosopher has eaten enough times
  • Millisecond-precision timing via a custom sleep helper to avoid missing the death deadline

Technical Challenges

  • Preventing deadlock when every philosopher tries to pick up forks at once, without serializing them so heavily that the simulation stops behaving concurrently
  • Detecting a philosopher's death within the required timing precision without a busy-wait loop burning CPU or a long sleep call missing the deadline
  • Protecting shared state, last meal time and the death flag, from race conditions between each philosopher thread and the monitor thread

Engineering Decisions

  • Used a dedicated monitor thread instead of having each philosopher check its own death condition, since a philosopher blocked waiting for a fork can't reliably watch its own clock
  • Broke long sleeps into short intervals rather than a single sleep call, trading a small amount of CPU overhead for the timing precision the monitor depends on
  • Guarded shared timing state with mutexes rather than atomics, keeping the synchronization model consistent with the rest of the fork-locking design

Technologies Used

C with POSIX threads (pthread) and mutexes for concurrency, built and tested on Linux.

Lessons Learned

  • Built a practical understanding of thread synchronization and the classic deadlock/starvation trade-offs beyond the textbook description
  • Learned how much correctness in a real-time-constrained concurrent system depends on timing precision, not just locking correctness
  • Practiced isolating shared mutable state and reasoning carefully about which thread is allowed to touch it and when