Skip to content

Instantly share code, notes, and snippets.

@tecnowilliam
Created July 4, 2020 00:27
Show Gist options
  • Select an option

  • Save tecnowilliam/0946e57956f22eb3eee15da1e67ce17c to your computer and use it in GitHub Desktop.

Select an option

Save tecnowilliam/0946e57956f22eb3eee15da1e67ce17c to your computer and use it in GitHub Desktop.
% Compile: c(frequency_hardened).
-module(frequency_hardened).
-author("William Vargas").
-version("1.0").
-export([init/0, start/0, stop/0, allocate/0, deallocate/1, tests/0]).
% Register the server as frequency_hardened
-spec start() -> ?MODULE.
start() ->
register(?MODULE, spawn(?MODULE, init, [])).
% Stop the frequency_hardened server
-spec stop() -> any().
stop() ->
?MODULE ! {request, self(), stop},
io:format("Server stopped~n"),
reply(1000).
% Initialize the server
-spec init() -> any().
init() ->
process_flag(trap_exit, true),
Frequencies = {get_frequencies(), []},
io:format("Server started~n"),
loop(Frequencies).
% Get the frequencies
-spec get_frequencies() -> list(integer()).
get_frequencies() -> [10,11,12,13,14,15].
% Reply/timeout any message
-spec reply(integer()) -> any().
reply(Timeout) ->
receive
{reply, Reply} ->
io:format("~w~n",[Reply]),
Reply;
_ -> ok
after Timeout ->
clear(),
{error, timeout}
end.
% Clear all the mailbox
-spec clear() -> ok.
clear() ->
receive
_Msg -> clear()
after 0 ->
io:format("Mailbox cleared~n"),
ok
end.
% Process the server messages
-spec loop(list(integer())) -> any().
loop(Frequencies) ->
io:format("Frequencies: ~w~n", [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, {deallocate_ok, -Freq}},
loop(NewFrequencies);
{request, _Pid, overload} ->
timer:sleep(1000),
loop(Frequencies);
{'EXIT', Pid, _Reason} ->
NewFrequencies = exited(Frequencies, Pid),
loop(NewFrequencies);
{request, _Pid, stop} ->
% Pid ! {reply, stopped}
stop()
end.
% Functional interface: allocate
-spec allocate() -> any().
allocate() ->
?MODULE ! {request, self(), allocate},
reply(1000).
% Allocate a frequency
-spec allocate(list(), pid()) -> tuple().
allocate({[], Allocated}, _Pid) ->
{{[], Allocated}, {allocate_error, no_frequency}};
allocate({[Freq|Free], Allocated}, Pid) ->
link(Pid),
{{Free, [{Freq, Pid}|Allocated]}, {allocate_ok, Freq}}.
% Functional interface: deallocate
-spec deallocate(integer()) -> any().
deallocate(Freq) ->
?MODULE ! {request, self(), {deallocate, Freq}},
reply(1000).
% Deallocate a frequency
-spec deallocate(tuple(), integer()) -> tuple().
deallocate({Free, Allocated}, Freq) ->
case lists:keyfind(Freq, 1, Allocated) of
{Freq, Pid} ->
NewAllocated = lists:delete({Freq, Pid}, Allocated),
unlink(Pid),
{[Freq|Free], NewAllocated};
_ ->
{Free, Allocated}
end.
% Exit from a frequency
-spec exited({list(), list()}, string()) -> tuple().
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.
% Tests
-spec tests() -> any().
tests() ->
% Test: Clean mailbox
clear(),
% Test: Start the server
start(),
% Test: Allocate 10,11 and 12
allocate(), allocate(), allocate(),
% Test: Deallocate 11
deallocate(11),
% Test: Allocate 11 and 13
allocate(), allocate(),
% Test: Overloaded server
io:format("Test overloaded server~n"),
?MODULE ! {request, self(), overload},
reply(1000).
% Compile: c(scenario).
-module(scenario).
-author("William Vargas").
-version("1.0").
-export([start_server/0, stop_server/0, start_client/0, stop_client/1, client/2, tests/0]).
% Launch the server for the clients
-spec start_server() -> any().
start_server() ->
frequency_hardened:start().
% Stop the server for the clients
-spec stop_server() -> any().
stop_server() ->
whereis(frequency_hardened) ! {request, self(), stop}.
% Start a new client
-spec start_client() -> integer().
start_client() ->
Client = list_to_atom(lists:flatten(io_lib:format("~p", [rand:uniform(1000)]))),
io:format("New client: ~w~n", [Client]),
register(Client, spawn_link(?MODULE, client, [Client, []])),
Client.
% Stop a client
-spec stop_client(integer()) -> any().
stop_client(Client) ->
whereis(Client) ! {request, self(), stop}.
% Allocate or desallocate a frequency to the client
-spec send_message(integer(), list()) -> any().
send_message(Client, Freqs) ->
case rand:uniform(2) of
1 ->
{allocate_ok, Freq} = frequency_hardened:allocate(),
io:format("Frequency ~w allocated to client ~w.~n", [Freq, Client]),
timer:sleep(2000),
client(Client, [Freq|Freqs]);
2 ->
Len = length(Freqs),
case Len of
0 ->
io:format("No frequencies to deallocate by client ~w.~n", [Client]),
timer:sleep(2000),
client(Client, Freqs);
_ ->
Freq = lists:nth(rand:uniform(Len),Freqs),
frequency_hardened:deallocate(Freq),
io:format("Frequency ~w deallocated by client ~w.~n", [Freq, Client]),
timer:sleep(2000),
client(Client, lists:delete(Freq,Freqs))
end
end.
% Manage the client options
-spec client(integer(), list()) -> any().
client(Client, Freqs) ->
receive
{request, _Pid, stop} ->
io:format("~w stopped~n", [Client])
after 0 ->
case is_pid(whereis(frequency_hardened)) of
true ->
send_message(Client, Freqs);
_ ->
io:format("Server is down, retrying... ~n"),
timer:sleep(1000),
client(Client, Freqs)
end
end.
% Tests
-spec tests() -> any().
tests() ->
% Test: Start the server
start_server(),
% Test: Start a client
Client = start_client(),
% Test: Retry when the server stop
timer:sleep(10000),
stop_server(),
timer:sleep(3000),
start_server(),
timer:sleep(5000),
% Test: Stop the client
stop_client(Client),
% Test: Stop the server
stop_server().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment