If you have ever seen Dining Philosophers before, then you know that there are a few ways to do it. My implementation creates philosopher and fork processes which communicate with message passing.
I was having a lot of program with format on the fork and philosopher processes, but I figured it out myself and now I will share the finished code. I'm a beginner fyi. Have a nice day.
-module(df).
-export([start/0, fork/2, philosopher/5]).
start() ->
SpawnForks = spawnForks([1,2,3,4], []),
SpawnForks2 = lists:reverse(SpawnForks),
main(SpawnForks2, [], 1).
%Creates a list of spawned fork processes
spawnForks([], ForkList) -> ForkList;
spawnForks([Head|Tail], ForkList) ->
Fork = spawn(df,fork,[Head, "down"]),
spawnForks(Tail, [Fork|ForkList]).
%Philosopher 4 will have a different fork order
main(ForkList, PhilList, Count) when Count =:= 4 ->
Fork1 = lists:nth(Count, ForkList),
Fork2 = lists:nth(1, ForkList),
%arguments to the philosopher: (Count = PhilosopherNumber, 2 = number of times to eat, fork1, fork2, self
Philosopher = spawn(df, philosopher, [Count, 2, Fork1, Fork2, self()]),
await([Philosopher|PhilList], ForkList);
%philosophers 1-3 will have standard fork order.
main(ForkList, PhilList, Count) ->
Fork1 = lists:nth(Count, ForkList),
Fork2 = lists:nth(Count + 1, ForkList),
%arguments to the philosopher: (Count = PhilosopherNumber, 2 = number of times to eat, fork1, fork2, self
Philosopher = spawn(df, philosopher, [Count, 2, Fork2, Fork1, self()]),
Count2 = Count + 1,
main(ForkList, [Philosopher|PhilList], Count2).
%await waits for philosophers to be done and shuts down the fork processes
await([],[])-> ok;
await([],[Head|Tail]) ->
Head ! {shutdown}, await([], Tail);
await([Pid|Rest], ForkList)->
receive {done, Pid} ->
await(Rest, ForkList)
end.
%a fork process
%when state == down, accept pickup messages. After receiving a pickup request,
%send an authorization. Print "fork up", and set fork State to up.
fork(Fork,State) when State =:= "down" ->
receive
{shutdown} -> exit(normal);
{pickup, Pid, Philosopher} ->
io:format("Philosopher ~p picks up Fork ~p.\n",[Philosopher,Fork]),
Pid ! {getfork, self()},
fork(Fork,"up")
end;
%when state == up, accept putdown messages. After receiving a putdown request,
%send an authorization. Print "fork up" and set fork State to up.
fork(Fork,State) when State =:= "up" ->
receive
{shutdown} -> exit(normal);
{putdown, Pid, Philosopher} ->
Pid ! {takefork, self()},
io:format("Philosopher ~p puts down Fork ~p.\n",[Philosopher,Fork]),
fork(Fork,"down")
end.
%a philosopher process
philosopher(_, Times, _,_, Main) when Times =:= 0 -> Main ! {done, self()}, exit(normal);
philosopher(Philosopher, Times, Fork1, Fork2, Main)->
io:format("Philosopher ~p is thinking.\n",[Philosopher]),
%request fork pickups on adjacent forks. wait for authorization messages and print "eating"
Fork1 ! {pickup, self(), Philosopher},
receive
{getfork, Fork1} ->
Fork2 ! {pickup, self(), Philosopher}
end,
receive
{getfork, Fork2} ->
io:format("Philosopher ~p is eating.\n",[Philosopher]),
Fork1 ! {putdown, self(), Philosopher},
Fork2 ! {putdown, self(), Philosopher},
receive
{takefork, Fork1} -> ok;
{takefork, Fork2} -> ok
end,
Times2 = Times - 1,
philosopher(Philosopher, Times2, Fork1, Fork2, Main)
end.