Skip to content

Instantly share code, notes, and snippets.

@eglyph
Created April 18, 2017 04:09
Show Gist options
  • Select an option

  • Save eglyph/413ba6bbf1e729ba4fc402318cf05163 to your computer and use it in GitHub Desktop.

Select an option

Save eglyph/413ba6bbf1e729ba4fc402318cf05163 to your computer and use it in GitHub Desktop.
-module(frequency).
-export([start/0,allocate/0,deallocate/1,stop/0]).
-export([init/0]).
-compile(export_all).
%% Usage:
%% SupervisorPid = spawn(frequency, super, []).
%% frequency:run_clients(7, frequency).
%% ... see clients working ...
%% ... using observer:start() find and kill frequency:loop()
%% ... see server restarted, clients working...
%% a simple supervisor that restarts a died server
super() ->
process_flag(trap_exit, true),
ServerPid = spawn_link(frequency, init, []),
register(frequency, ServerPid),
io:format("Started supervisor, server pid is ~p~n", [ServerPid]),
receive
{'EXIT', ServerPid, Why} ->
io:format("A server has terminated, reason: '~p', restarting~n",
[Why]),
super()
end.
start() ->
ServerPid = spawn(frequency, init, []),
register(frequency, ServerPid),
ServerPid.
init() ->
process_flag(trap_exit, true), %%% ADDED
Frequencies = {get_frequencies(), []},
loop(Frequencies).
% Hard Coded
get_frequencies() -> [10,11,12,13,14,15,16,17,18,19].
%% The Main Loop
loop(Frequencies) ->
receive
{request, Pid, allocate} ->
{NewFrequencies, Reply} = allocate(Frequencies, Pid),
Pid ! {reply, Reply},
loop(NewFrequencies);
{request, Pid , {deallocate, Freq}} ->
NewFrequencies = deallocate(Frequencies, Freq),
Pid ! {reply, ok},
loop(NewFrequencies);
{request, Pid, stop} ->
Pid ! {reply, stopped};
{'EXIT', Pid, _Reason} -> %%% CLAUSE ADDED
NewFrequencies = exited(Frequencies, Pid),
loop(NewFrequencies)
end.
%% Functional interface
allocate() ->
frequency ! {request, self(), allocate},
receive
{reply, Reply} -> Reply
end.
deallocate(Freq) ->
frequency ! {request, self(), {deallocate, Freq}},
receive
{reply, Reply} -> Reply
end.
stop() ->
frequency ! {request, self(), stop},
receive
{reply, Reply} -> Reply
end.
%% The Internal Help Functions used to allocate and
%% deallocate frequencies.
allocate({[], Allocated}, _Pid) ->
{{[], Allocated}, {error, no_frequency}};
allocate({[Freq|Free], Allocated}, Pid) ->
link(Pid), %%% ADDED
{{Free, [{Freq, Pid}|Allocated]}, {ok, Freq}}.
deallocate({Free, Allocated}, Freq) ->
{value,{Freq,Pid}} = lists:keysearch(Freq,1,Allocated), %%% ADDED
unlink(Pid), %%% ADDED
NewAllocated=lists:keydelete(Freq, 1, Allocated),
{[Freq|Free], NewAllocated}.
exited({Free, Allocated}, Pid) -> %%% FUNCTION ADDED
case lists:keysearch(Pid,2,Allocated) of
{value,{Freq,Pid}} ->
NewAllocated = lists:keydelete(Freq,1,Allocated),
{[Freq|Free],NewAllocated};
false ->
{Free,Allocated}
end.
%% --------------------------------------------------
%% Client functionality
% flush a mailbox
clear() -> receive
_Msg -> clear()
after 0 -> ok
end.
%% a generic function that sends a message somewhere and expects an answer
%% timeout param is added for easier testing
call(Who, Message, Timeout) ->
process_flag(trap_exit, true),
TargetPid = whereis(Who),
link(TargetPid), % allows passing a registered name
Who ! Message,
receive
{'EXIT', _Pid, _Why} -> {error, server_died};
{reply, Reply} -> unlink(TargetPid),
Reply
after Timeout -> {error, timeout}
end.
alloc(ServerPid) -> call(ServerPid, {request, self(), allocate}, 1000).
free(ServerPid, Freq) ->
call(ServerPid, {request, self(), {deallocate, Freq}}, 1000).
%% a client process emulator
client(ClientNum, ServerPid) ->
Timeout = 5000 + rand:uniform(2000),
Reply = alloc(ServerPid),
case Reply of
{ok, Freq} ->
io:format("Client #~p allocated ~p~n", [ClientNum, Freq]),
Res = free(ServerPid, Freq),
io:format("Client #~p deallocated frequency, result is ~p~n",
[ClientNum, Res]),
timer:sleep(Timeout),
client(ClientNum, ServerPid);
{error, Why} ->
io:format("Client #~p received an error ~p, exiting~n",
[ClientNum, Why]),
exit
end.
%% spwans a given number of clients simulator
%% Server is a registered name
run_clients(0, _) -> ok;
run_clients(Num, Server) ->
spawn(?MODULE, client, [Num, Server]),
run_clients(Num - 1, ServerPid).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment