Thursday, January 29, 2009

Erlang.org course exercises for spawning processes and exchanging messages

The Erlang.org website contains a number of programming exercises dealing with spawning processes and exchanging messages between them:

Write a function which starts 2 processes, and sends a message M times forwards and backwards between them. After the messages have been sent the processes should terminate gracefully.

%% Erlang exercises: Interaction between processes and concurrency, problem number 1.
%%
%% Write a function which starts 2 processes, and sends a message M times forewards and backwards between them.
%% After the messages have been sent the processes should terminate gracefully.
%%
%% See http://erlang.org/course/exercises.html for details.

-module(processes1).
-author('Cayle Spandon').

-export([solve/0, solve/1]).

solve() ->
solve(10).

solve(NrMsgs) ->
Pid1 = spawn(fun() -> process_1() end),
spawn(fun() -> process_2(Pid1, NrMsgs) end),
ok.

process_1() ->
io:format("Process 1 starts.~n"),
process_1_loop(),
io:format("Process 1 ends.~n").

process_1_loop() ->
receive
{From, {message, More}} ->
io:format("Process 1 received message ~p.~n", [More]),
io:format("Process 1 sends message_reply.~n"),
From ! {self(), {message_reply}},
if
More ->
process_1_loop();
true ->
ok
end
end.

process_2(Pid1, NrMsgs) ->
io:format("Process 2 starts.~n"),
process_2_loop(Pid1, 1, NrMsgs),
io:format("Process 2 ends.~n").

process_2_loop(Pid1, MsgNr, NrMsgs) ->
io:format("Process 2 sends message nr ~p.~n", [MsgNr]),
More = (MsgNr < NrMsgs),
Pid1 ! {self(), {message, More}},
receive
{Pid1, {message_reply}} ->
io:format("Process 2 received message_reply.~n"),
if
More ->
process_2_loop(Pid1, MsgNr + 1, NrMsgs);
true ->
ok
end
end.


Write a function which starts N processes in a ring, and sends a message M times around all the processes in the ring. After the messages have been sent the processes should terminate gracefully.

%% Erlang exercises: Interaction between processes and concurrency, problem number 2.
%%
%% Write a function which starts N processes in a ring, and sends a message M times around all the processes in the
%% ring. After the messages have been sent the processes should terminate gracefully.
%%
%% See http://erlang.org/course/exercises.html for details.

-module(processes2).
-author('Cayle Spandon').

-export([solve/0, solve/2]).

solve() ->
solve(3, 2).

solve(NrProcesses, NrLoops) ->
Pids = [spawn(fun() -> process_init(ProcessNr, NrProcesses) end) || ProcessNr <- lists:seq(1, NrProcesses)],
[FirstPid | _] = Pids,
set_next_pids(FirstPid, Pids),
FirstPid ! {message, 1, NrLoops}.

set_next_pids(FirstPid, [Pid1, Pid2 | OtherPids]) ->
Pid1 ! {set_next_pid, Pid2},
set_next_pids(FirstPid, [Pid2 | OtherPids]);

set_next_pids(FirstPid, [LastPid]) ->
LastPid ! {set_next_pid, FirstPid}.

process_init(ProcessNr, NrProcesses) ->
io:format("Process ~p of ~p: waiting for set_next_pid message~n", [ProcessNr, NrProcesses]),
receive
{set_next_pid, NextPid} -> ok
end,
io:format("Process ~p of ~p: next pid is ~p~n", [ProcessNr, NrProcesses, NextPid]),
process_loop(ProcessNr, NrProcesses, NextPid).

process_loop(ProcessNr, NrProcesses, NextPid) ->
receive
{message, Loop, NrLoops} ->
io:format("Process ~p of ~p: receive message with loop ~p of ~p~n", [ProcessNr, NrProcesses, Loop, NrLoops]),
if
ProcessNr == NrProcesses ->
NewLoop = Loop + 1;
true ->
NewLoop = Loop
end,
NextPid ! {message, NewLoop, NrLoops},
if
Loop < NrLoops ->
process_loop(ProcessNr, NrProcesses, NextPid);
true ->
io:format("Process ~p: ends~n", [ProcessNr]),
ok
end
end.


Write a function which starts N processes in a star, and sends a message to each of them M times. After the messages have been sent the processes should terminate gracefully.

%% Erlang exercises: Interaction between processes and concurrency, problem number 3.
%%
%% Write a function which starts N processes in a star, and sends a message to each of them M times. After the messages
%% have been sent the processes should terminate gracefully.
%%
%% See http://erlang.org/course/exercises.html for details.

-module(processes3).
-author('Cayle Spandon').

-export([solve/0, solve/2]).

solve() ->
solve(3, 2).

solve(NrProcesses, NrLoops) ->
SpokePids = [spawn(fun() -> spoke_process_start(ProcessNr) end) || ProcessNr <- lists:seq(1, NrProcesses)],
spawn_link(fun() -> hub_process_start(SpokePids, NrLoops) end),
ok.

spoke_process_start(ProcessNr) ->
io:format("spoke_process ~p: start~n", [ProcessNr]),
spoke_process_loop(ProcessNr).

spoke_process_loop(ProcessNr) ->
receive
{From, ping_request, More} ->
io:format("spoke_process ~p: receive ping_request~n", [ProcessNr]),
io:format("spoke_process ~p: send ping_reply~n", [ProcessNr]),
From ! {self(), ping_reply}
end,
if
More ->
spoke_process_loop(ProcessNr);
true ->
io:format("spoke_process ~p: end~n", [ProcessNr]),
ok
end.

hub_process_start(SpokePids, NrLoops) ->
io:format("hub_process: start~n"),
hub_process_loop(SpokePids, NrLoops).

hub_process_loop(_SpokePids, 0) ->
io:format("hub_process: end~n"),
ok;

hub_process_loop(SpokePids, LoopNr) ->
More = (LoopNr > 1),
io:format("hub_process: loop ~p~n", [LoopNr]),
ping_all_spokes(SpokePids, More),
hub_process_loop(SpokePids, LoopNr - 1).

ping_all_spokes([], _More) ->
ok;

ping_all_spokes([SpokePid | RemainingSpokePids], More) ->
ping_spoke(SpokePid, More),
ping_all_spokes(RemainingSpokePids, More).

ping_spoke(SpokePid, More) ->
io:format("hub_process: send ping_request spoke ~p~n", [SpokePid]),
SpokePid ! {self(), ping_request, More},
receive
{From, ping_reply} ->
io:format("hub_process: receive ping_reply from spoke ~p~n", [From])
end,
ok.

No comments: