forked from seven1m/30-days-of-elixir
-
Notifications
You must be signed in to change notification settings - Fork 0
/
17-dining-philosophers.exs
109 lines (88 loc) · 3.5 KB
/
17-dining-philosophers.exs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
defmodule Table do
@moduledoc """
h2. Problem
http://rosettacode.org/wiki/Dining_philosophers
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 16-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
...
"""
defrecord Philosopher, 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)
end
def manage_resources(forks, waiting // []) do
# distribute forks to waiting philosophers
if length(waiting) > 0 do
names = lc {_, phil} inlist waiting, do: phil.name
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]}
end
end
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)
end
end
end
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)
end
dine(phil, table)
end
def eat(phil, forks, table) do
phil = phil.ate(phil.ate + 1)
IO.puts "#{phil.name} is eating (count: #{phil.ate})"
:timer.sleep(:random.uniform(1000))
IO.puts "#{phil.name} is done eating"
send table, {:give_up_seat, forks, phil}
phil
end
def think(phil, _) do
IO.puts "#{phil.name} is thinking (count: #{phil.thunk})"
:timer.sleep(:random.uniform(1000))
phil.thunk(phil.thunk + 1)
end
end
:random.seed(:erlang.now)
Table.simulate