Skip to content

Instantly share code, notes, and snippets.

@Xdeon
Created July 7, 2020 06:25
Show Gist options
  • Save Xdeon/5676bb037b816b64bf19a7a6992a2bdc to your computer and use it in GitHub Desktop.
Save Xdeon/5676bb037b816b64bf19a7a6992a2bdc to your computer and use it in GitHub Desktop.
frequency server revised
-module(frequency).
-export([start/0, allocate/0, deallocate/1, stop/0, clear/0, clear_with_print/0, init/0]).
start() ->
Pid = spawn(?MODULE, init, []),
register(?MODULE, Pid),
Pid.
%% Add clear at beginning of each API call to eliminate unexpected messages in mailbox
%% better use combination of erlang:make_ref() and "let it fail"
allocate() ->
%clear(),
?MODULE ! {request, self(), allocate},
client_receive().
deallocate(Freq) ->
%clear(),
?MODULE ! {request, self(), {deallocate, Freq}},
client_receive().
stop() ->
% clear(),
?MODULE ! {request, self(), stop},
client_receive().
client_receive() ->
receive
{reply, Reply} -> Reply
after 500 ->
{error, timeout}
end.
%% only clear messages that following protocol defined here instead of any messages
clear() ->
receive
{reply, _} -> clear()
after 0 -> ok
end.
clear_with_print() ->
receive
{reply, _} = Msg ->
io:format("~p~n", [Msg]),
clear_with_print()
after 0 -> ok
end.
init() ->
Frequencies = {get_frequencies(), []},
loop(Frequencies).
get_frequencies() ->
[10, 11, 12, 13, 14, 15].
%% uncomment timer:sleep/1 to simulate overloaded server
loop(Frequencies) ->
receive
{request, Pid, allocate} ->
% timer:sleep(1000),
{NewFrequencies, Reply} = allocate(Frequencies, Pid),
Pid ! {reply, Reply},
loop(NewFrequencies);
{request, Pid, {deallocate, Freq}} ->
% timer:sleep(1000),
NewFrequencies = deallocate(Frequencies, Freq),
Pid ! {reply, ok},
loop(NewFrequencies);
{request, Pid, stop} ->
% timer:sleep(1000),
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) ->
{{Free, [{Freq, Pid}|Allocated]}, {ok, Freq}}.
deallocate({Free, Allocated}, Freq) ->
case lists:keymember(Freq, 1, Allocated) of
true ->
{[Freq|Free], lists:keydelete(Freq, 1, Allocated)};
false ->
{Free, Allocated}
end.
% tests
-include_lib("eunit/include/eunit.hrl").
frequency_test() ->
Freq = start(),
% test naming
?assertEqual(Freq, whereis(?MODULE)),
% test allocate
?assertEqual({ok, 10}, allocate()),
% test duplicate allocate
?assertEqual({error, already_allocated}, allocate()),
% test inproper deallocate
?assertEqual(ok, deallocate(11)),
?assertEqual({error, already_allocated}, allocate()),
% test proper deallocate
?assertEqual(ok, deallocate(10)),
?assertEqual({ok, 10}, allocate()),
% test stop
?assertEqual(stopped, stop()),
ok.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment