Last active
July 11, 2020 02:12
-
-
Save Xdeon/ad98a90a4c9dc9332c9d28edc6d15d3a to your computer and use it in GitHub Desktop.
frequency server hardened with links
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(frequency_hardened). | |
-export([start/0, allocate/0, deallocate/1, stop/0, init/0]). | |
start() -> | |
Pid = spawn(?MODULE, init, []), | |
register(?MODULE, Pid), | |
Pid. | |
allocate() -> | |
?MODULE ! {request, self(), allocate}, | |
client_receive(). | |
deallocate(Freq) -> | |
?MODULE ! {request, self(), {deallocate, Freq}}, | |
client_receive(). | |
stop() -> | |
?MODULE ! {request, self(), stop}, | |
client_receive(). | |
client_receive() -> | |
receive | |
{reply, Reply} -> Reply | |
after 500 -> | |
{error, timeout} | |
end. | |
init() -> | |
erlang:process_flag(trap_exit, true), | |
Frequencies = {get_frequencies(), []}, | |
loop(Frequencies). | |
get_frequencies() -> | |
[10, 11, 12, 13, 14, 15]. | |
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); | |
{'EXIT', Pid, _Reason} -> | |
NewFrequencies = exited(Frequencies, Pid), | |
loop(NewFrequencies); | |
{request, Pid, stop} -> | |
Pid ! {reply, stopped} | |
end. | |
allocate({Freqs, Allocated}, Pid) -> | |
case lists:keymember(Pid, 2, Allocated) of | |
true -> {{Freqs, Allocated}, {error, already_allocated}}; | |
false -> do_allocate({Freqs, Allocated}, Pid) | |
end. | |
do_allocate({[], Allocated}, _Pid) -> | |
{{[], Allocated}, {error, no_frequency}}; | |
do_allocate({[Freq|Free], Allocated}, Pid) -> | |
link(Pid), | |
{{Free, [{Freq, Pid}|Allocated]}, {ok, Freq}}. | |
deallocate({Free, Allocated}, Freq) -> | |
case lists:keytake(Freq, 1, Allocated) of | |
{value, {Freq, Pid}, NewAllocated} -> | |
unlink(Pid), | |
{[Freq|Free], NewAllocated}; | |
false -> | |
{Free, Allocated} | |
end. | |
exited({Free, Allocated}, Pid) -> | |
case lists:keytake(Pid, 2, Allocated) of | |
{value, {Freq, Pid}, NewAllocated} -> | |
{[Freq|Free], NewAllocated}; | |
false -> | |
{Free, Allocated} | |
end. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(scenario). | |
-export([setup/0, stop/1, client/2, kill_server/0, random_elem/1]). | |
% Use this module to exercise the behaviour of the | |
% hardened frequency server. | |
% Calling setup will launch the server and two clients: alice and bob. | |
setup() -> | |
frequency_hardened:start(), | |
[spawn(?MODULE, client, [Id, []]) || Id <- names()]. | |
kill_server() -> | |
exit(whereis(frequency_hardened), kill). | |
stop(Clients) -> | |
[Pid ! stop || Pid <- Clients], | |
catch frequency_hardened:stop(), | |
ok. | |
names() -> | |
[alice, bob]. | |
% A client, parametrised by its name (optional, but useful instrumentation), | |
% and the list of frequencies currently allocated to that process. Needed | |
% to produce calls to deallocate/1 that don't fail. | |
% Could also | |
% - parameterise on the ratio of allocates to deallocates | |
% - deal with case when no frequencies available: here a client fails | |
% - add stop commands. | |
client(Id, Freqs) -> | |
erlang:process_flag(trap_exit, true), | |
loop(Id, Freqs). | |
loop(Id, Freqs) -> | |
receive | |
{'EXIT', _, _} -> | |
io:format("Frequency server down. Exit.~n"), | |
% since the server is down, all API calls would fail | |
% hope the client shutdown gracefully | |
ok; | |
stop -> | |
io:format("client ~w asked to stop. Exit.~n", [Id]), | |
ok | |
% Notice it is very likely that API calls to frequency server fail due to the name can not be found | |
% when we kill the server manually | |
% this can not be avoided in current implementation | |
after 0 -> | |
case rand:uniform(2) of | |
1 -> | |
case frequency_hardened:allocate() of | |
{ok, Freq} -> | |
io:format("Frequency ~w allocated to client ~w.~n", [Freq, Id]), | |
timer:sleep(1000), | |
loop(Id, [Freq|Freqs]); | |
{error, no_frequency} -> | |
io:format("No frequency available for client ~w. Exit.~n", [Id]), | |
exit(no_frequency); | |
{error, already_allocated} -> | |
io:format("Frequency already allocated to client ~w. Continue.~n", [Id]), | |
timer:sleep(1000), | |
loop(Id, Freqs) | |
end; | |
2 -> | |
Len = length(Freqs), | |
case Len of | |
0 -> | |
io:format("No frequencies to deallocate by client ~w.~n", [Id]), | |
timer:sleep(1000), | |
loop(Id, Freqs); | |
_ -> | |
Freq = lists:nth(rand:uniform(Len), Freqs), | |
frequency_hardened:deallocate(Freq), | |
io:format("Frequency ~w deallocated by client ~w.~n", [Freq, Id]), | |
timer:sleep(1000), | |
loop(Id, lists:delete(Freq, Freqs)) | |
end | |
end | |
end. | |
% for debugging purposes: chooses a random element of a non-empty list. | |
random_elem([]) -> | |
empty; | |
random_elem(Xs) -> | |
Len = length(Xs), | |
lists:nth(rand:uniform(Len), Xs). |
Nice!!
Tip: you should take a look at
lists:keytake/3
🧐
Thanks for the hint!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice!!
Tip: you should take a look at
lists:keytake/3
🧐