I apologize if the code is hard to follow. This is the classic dining philosophers problem, where 5 philosophers are eating, but there are only 5 sticks - and you need two to eat.
These are the instructions, if anyone is interested: http://www.kth.se/polopoly_fs/1.260940!/Menu/general/column-content/attachment/philosophers.pdf
Anyways, here is the code, the chopstick process code:
-module(chopstick).
-export([start/0]).
start() ->
spawn_link(fun() -> init() end).
init() ->
available().
available() ->
receive
{request, From} ->
From ! granted,
gone();
quit ->
ok
end.
gone() ->
receive
returned ->
available();
quit ->
ok
end.
The philosopher process code:
-module(eater).
-import(timer, [sleep/1]).
-import(random, [uniform/1]).
-export([start/5, dream/5, eat/5, wait/5]).
start(Hungry, Right, Left, Name, Ctrl) ->
dream(Hungry, Right, Left, Name, Ctrl).
**%This was wrong, it should say start(Hungry, Right, Left, Name, Ctrl) ->
spawn_link(fun() -> dream(Hungry, Right, Left, Name, Ctrl) end).**
dream(Hungry, Right, Left, Name, Ctrl) ->
Time = 500+uniform:random(500), **%This was wrong, it should say random:uniform**
timer:sleep(Time),
Right! {request, self()},
Left! {request, self()},
%skicka {request, self()} till två pinnar
wait(Hungry, Right, Left, Name, Ctrl).
wait(Hungry, Right, Left, Name, Ctrl) ->
receive
granted ->
io:format("~s received a chopstick~n", [Name]),
receive
granted ->
io:format("~s received a chopstick~n", [Name]),
io:format("~s started eating~n", [Name]),
eat(Hungry, Right, Left, Name, Ctrl)
end;
_ -> wait(Hungry, Right, Left, Name, Ctrl)
end.
eat(Hungry, Right, Left, Name, Ctrl) ->
Time = 500+uniform:random(500), **%This was wrong, it should say random:uniform**
timer:sleep(Time),
Right! returned,
Left! returned,
io:format("~s put back two chopsticks~n", [Name]),
if
Hungry =< 1 ->
Ctrl ! done;
true ->
dream(Hungry-1, Right, Left, Name, Ctrl)
end.
And finally the host process:
-module(dinner).
-export([start/0]).
start() ->
spawn(fun() -> init() end).
init() ->
C1 = chopstick:start(),
C2 = chopstick:start(),
C3 = chopstick:start(),
C4 = chopstick:start(),
C5 = chopstick:start(),
Ctrl = self(),
eater:start(5, C1, C2, "Confucios", Ctrl), **% This is where it crashes**
eater:start(5, C2, C3, "Avicenna", Ctrl),
eater:start(5, C3, C4, "Plato", Ctrl),
eater:start(5, C4, C5, "Kant", Ctrl),
eater:start(5, C5, C1, "Descartes", Ctrl),
wait(5, [C1, C2, C3, C4, C5]).
wait(0, Chopsticks) ->
lists:foreach(fun(C) -> C ! quit end, Chopsticks);
wait(N, Chopsticks) ->
receive
done ->
wait(N-1, Chopsticks);
abort ->
erlang:exit(abort)
end.
Output:
11> dinner:start().
<0.85.0>
12>
=ERROR REPORT==== 10-Nov-2011::02:19:10 ===
Error in process <0.85.0> with exit value: {undef,[{uniform,random,[500]}, {eater,dream,5},{dinner,init,0}]}
Thanks a lot if you even read through all of this, I haven't learnt how to read the error reports of erlang yet. If you can, and want to tell me what it means please do.
Difficulty with the solution The above solution makes sure that no two neighboring philosophers can eat at the same time. But this solution can lead to a deadlock. This may happen if all the philosophers pick their left chopstick simultaneously. Then none of them can eat and deadlock occurs.
Deadlock could occur if every philosopher holds a left chopstick and waits perpetually for a right chopstick (or vice versa). Originally used as a means of illustrating the problem of deadlock, this system reaches deadlock when there is a 'cycle of unwarranted requests'.
Strategy: Every philosopher must request each of their (shared) chopsticks from a waiter, who may refuse the request at first in order to avoid a deadlock. For convenience, we assume that all philosophers request their left chopstick first, then their right chopstick.
Problem with this solution : This solution may lead to a deadlock under an interleaving that has all the philosophers pick up their left forks before any of them tries to pick up a right fork. In this case, all the Philosophers are waiting for the right fork but no one will execute a single instruction.
I think the problem is that you've got three modules: dinner
, eater
, and chopstick
, but try to call philospher:start
in your dinner:init/0
function. Try eater:start
instead.
The second problem is the order of module and function name when generating random numbers; replace uniform:random
with random:uniform
in your eater.erl
:
1> dinner:start().
<0.35.0>
Confucios received a chopstick
Confucios received a chopstick
Confucios started eating
Confucios put back two chopsticks
Confucios received a chopstick
Confucios received a chopstick
Confucios started eating
Confucios put back two chopsticks
Confucios received a chopstick
Confucios received a chopstick
Confucios started eating
Confucios put back two chopsticks
Confucios received a chopstick
Confucios received a chopstick
Confucios started eating
Confucios put back two chopsticks
Confucios received a chopstick
Confucios received a chopstick
Confucios started eating
Confucios put back two chopsticks
Avicenna received a chopstick
Avicenna received a chopstick
Avicenna started eating
...
This pretty quickly shows the third problem -- something we should have spotted from the first error report -- that the eaters aren't actually in their own processes. So edit eater.erl
so that the start()
function reads:
start(Hungry, Right, Left, Name, Ctrl) ->
spawn_link(fun() -> dream(Hungry, Right, Left, Name, Ctrl) end).
Now it works as intended:
1> dinner:start().
<0.35.0>
Confucios received a chopstick
Plato received a chopstick
Confucios received a chopstick
Confucios started eating
Descartes received a chopstick
Kant received a chopstick
Confucios put back two chopsticks
Avicenna received a chopstick
...
Thanks. This was good fun.
change wherever you are calling: to uniform:random/1
random:uniform/1
and the exception will be gone
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With