Find file
Fetching contributors…
Cannot retrieve contributors at this time
112 lines (90 sloc) 3.53 KB
defmodule Table do
@moduledoc """
h2. Problem
Five philosophers, Aristotle, Kant, Spinoza, Marx, and Russell (the tasks) spend their time
thinking and eating spaghetti. They eat at a round table with five individual seats.
For eating each philosopher needs two forks (the resources). There are five forks on the table,
one left and one right of each seat. When a philosopher cannot grab both forks it sits and waits.
Eating takes random time, then the philosopher puts the forks down and leaves the dining room.
After spending some random time thinking about the nature of the universe, he again becomes
hungry, and the circle repeats itself.
h2. My Solution
So, I cheated a little. I skipped modeling of the seats and fork placement, and instead modeled
forks as 5 discreet resources to be managed by the "Table". I figured philosophers can just
reach over and grab a fork, yeah? :-)
It turns out that message passing between processes made this very simple. Even adding "seats"
as resources to be managed wouldn't make this much more difficult.
h2. Sample Run
$ elixir 17-dining-philosophers.exs
1 philosopher waiting: Aristotle
1 philosopher waiting: Kant
Aristotle is eating (count: 1)
1 philosopher waiting: Spinoza
Kant is eating (count: 1)
2 philosophers waiting: Marx, Spinoza
3 philosophers waiting: Russell, Marx, Spinoza
Kant is done eating
Aristotle is done eating
defmodule Philosopher do
defstruct name: nil, ate: 0, thunk: 0
def simulate do
forks = [:fork1, :fork2, :fork3, :fork4, :fork5]
table = spawn_link(Table, :manage_resources, [forks])
spawn(Dine, :dine, [%Philosopher{name: "Aristotle"}, table])
spawn(Dine, :dine, [%Philosopher{name: "Kant" }, table])
spawn(Dine, :dine, [%Philosopher{name: "Spinoza" }, table])
spawn(Dine, :dine, [%Philosopher{name: "Marx" }, table])
spawn(Dine, :dine, [%Philosopher{name: "Russell" }, table])
receive do: (_ -> :ok)
def manage_resources(forks, waiting \\ []) do
# distribute forks to waiting philosophers
if length(waiting) > 0 do
names = for {_, phil} <- waiting, do:
IO.puts "#{length waiting} philosopher#{if length(waiting) == 1, do: '', else: 's'} waiting: #{Enum.join names, ", "}"
if length(forks) >= 2 do
[{pid, _} | waiting] = waiting
[fork1, fork2 | forks] = forks
send pid, {:eat, [fork1, fork2]}
receive do
{:sit_down, pid, phil} ->
manage_resources(forks, [{pid, phil} | waiting])
{:give_up_seat, free_forks, _} ->
forks = free_forks ++ forks
IO.puts "#{length forks} fork#{if length(forks) == 1, do: '', else: 's'} available"
manage_resources(forks, waiting)
defmodule Dine do
def dine(phil, table) do
send table, {:sit_down, self, phil}
receive do
{:eat, forks} ->
phil = eat(phil, forks, table)
phil = think(phil, table)
dine(phil, table)
def eat(phil, forks, table) do
phil = %{phil | ate: phil.ate + 1}
IO.puts "#{} is eating (count: #{phil.ate})"
IO.puts "#{} is done eating"
send table, {:give_up_seat, forks, phil}
def think(phil, _) do
IO.puts "#{} is thinking (count: #{phil.thunk})"
%{phil | thunk: phil.thunk + 1}