Skip to content

Instantly share code, notes, and snippets.

@jason-carter
Created April 20, 2017 21:14
Show Gist options
  • Save jason-carter/3b0e75f478c5997e811db500facaac84 to your computer and use it in GitHub Desktop.
Save jason-carter/3b0e75f478c5997e811db500facaac84 to your computer and use it in GitHub Desktop.
FutureLearn Concurrent Programming In Erlang 2.10: Supervision in the frequency server
%% Based on code from
%% Erlang Programming
%% Francecso Cesarini and Simon Thompson
%% O'Reilly, 2008
%% http://oreilly.com/catalog/9780596518189/
%% http://www.erlangprogramming.org/
%% (c) Francesco Cesarini and Simon Thompson
-module(freqsupervisor).
-export([start_simulation/0]).
-export([init_supervisor/0,start_supervisor/0]).
-export([init_client/2,start_client/3]).
-export([init_server/1]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Testing/simulation functions
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start_simulation() ->
start_supervisor(),
timer:sleep(2000),
start_client(client1, frequency_server1, [allocate, wait, deallocate, wait]),
start_client(client2, frequency_server1, [allocate, wait, deallocate, wait]).
%start_client(client3, frequency_server2, [allocate, wait, deallocate, wait]),
%start_client(client4, frequency_server2, [allocate, wait, deallocate, wait]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Supervisor functions
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start_supervisor() ->
register(frequency_supervisor, spawn(freqsupervisor, init_supervisor, [])).
init_supervisor() ->
process_flag(trap_exit, true),
register(frequency_server1, spawn_link(freqsupervisor, init_server, [[10,11,12,13,14,15]])),
%register(frequency_server2, spawn_link(freqsupervisor, init_server, [[20,21,22,23,24,25]])),
supervisor_loop().
supervisor_loop() ->
receive
{'EXIT', _Pid, Reason} ->
io:format("[~w] Supervisor detected server killed (reason: ~w), attempting restart...~n",[self(), Reason]),
register(frequency_server1, spawn_link(freqsupervisor, init_server, [[10,11,12,13,14,15]])),
supervisor_loop();
stop ->
io:format("[~w] Supervisor stopping~n", [self()]);
UnknownMsg ->
io:format("[~w] Supervisor dropping unknown message: ~w~n",[self(), UnknownMsg]),
supervisor_loop()
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Server functions
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% should only be started by the supervisor
init_server(FrequencyList) ->
process_flag(trap_exit, true), %%% ADDED
Frequencies = {FrequencyList, []},
server_loop(Frequencies).
server_loop(Frequencies) ->
receive
{request, Pid, allocate} ->
{NewFrequencies, Reply} = server_allocate(Frequencies, Pid),
Pid ! {reply, Reply},
server_loop(NewFrequencies);
{request, Pid , {deallocate, Freq}} ->
NewFrequencies = server_deallocate(Frequencies, Freq),
Pid ! {reply, ok},
server_loop(NewFrequencies);
{request, Pid, stop} ->
Pid ! {reply, stopped};
{'EXIT', Pid, Reason} ->
io:format("[~w] Server detected client ~w died (reason: ~w), deallocating frequencies~n",[self(), Pid, Reason]),
NewFrequencies = exited(Frequencies, Pid),
server_loop(NewFrequencies)
end.
%% The Internal Help Functions used to allocate and deallocate frequencies.
server_allocate({[], Allocated}, _Pid) ->
{{[], Allocated}, {error, no_frequency}};
server_allocate({[Freq|Free], Allocated}, Pid) ->
link(Pid),
{{Free, [{Freq, Pid}|Allocated]}, {ok, Freq}}.
server_deallocate({Free, Allocated}, Freq) ->
{value,{Freq,Pid}} = lists:keysearch(Freq,1,Allocated),
unlink(Pid),
NewAllocated=lists:keydelete(Freq, 1, Allocated),
{[Freq|Free], NewAllocated}.
exited({Free, Allocated}, Pid) ->
case lists:keysearch(Pid,2,Allocated) of
{value,{Freq,Pid}} ->
NewAllocated = lists:keydelete(Freq,1,Allocated),
{[Freq|Free],NewAllocated};
false ->
{Free,Allocated}
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Client functions
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
start_client(ClientName, ServerName, Commands) ->
register(ClientName, spawn(freqsupervisor, init_client, [ServerName, Commands])).
init_client(ServerName, Commands) ->
process_flag(trap_exit, true),
client_loop(ServerName, Commands).
client_loop(ServerName, Commands) -> client_loop(ServerName, Commands, [], []).
client_loop(ServerName, [], Commands, Frequencies) ->
% Start the loop agin, and loop forever (until exit signal)
client_loop(ServerName, lists:reverse(Commands), [], Frequencies);
client_loop(ServerName, [Command |CommandsRemaining], CommandsProcessed, Frequencies) ->
receive
{'EXIT', Pid, normal} -> io:format("[~w] Client exited normally by ~w~n", [self(), Pid]);
{'EXIT', Pid, Reason} -> io:format("[~w] Client killed by ~w, reason: ~w~n",[self(), Pid, Reason]);
stop -> io:format("[~w] Client stopped~n", [self()]);
UnknownMsg -> io:format("[~w] Client dropping unknown message: ~w~n",[self(), UnknownMsg])
after 0 ->
case Command of
allocate ->
{ok, Freq} = client_allocate(ServerName),
io:format("[~w] Allocated frequency: ~w~n",[self(),Freq]),
client_loop(ServerName, CommandsRemaining, [Command | CommandsProcessed], [Freq | Frequencies]);
deallocate ->
case Frequencies of
[] ->
io:format("[~w] No frequencies to deallocate!~n", [self()]),
client_loop(ServerName, CommandsRemaining, [Command | CommandsProcessed], Frequencies);
[Freq | AllocatedFrequencies] ->
io:format("[~w] Deallocating frequency: ~w~n",[self(), Freq]),
client_deallocate(ServerName, Freq),
client_loop(ServerName, CommandsRemaining, [Command | CommandsProcessed], AllocatedFrequencies)
end;
wait ->
io:format("[~w] Sleeping~n", [self()]),
timer:sleep(5000),
client_loop(ServerName, CommandsRemaining, [Command | CommandsProcessed], Frequencies)
end
end.
%% Functional interfaces
client_allocate(ServerName) ->
ServerName ! {request, self(), allocate},
receive
{reply, Reply} -> Reply
end.
client_deallocate(ServerName, Freq) ->
ServerName ! {request, self(), {deallocate, Freq}},
receive
{reply, Reply} -> Reply
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment