Created
April 21, 2017 13:43
-
-
Save colinbankier/38e652f5a4beb32a6fe0a80cc8e67741 to your computer and use it in GitHub Desktop.
Frequency Server
This file contains hidden or 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
%% 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(frequency). | |
-export([start/0, init/0, allocate/0, deallocate/1]). | |
%% These are the start functions used to create and | |
%% initialize the server. | |
start() -> | |
case whereis(?MODULE) of | |
undefined -> register(?MODULE, spawn(?MODULE, init, [])); | |
_Pid -> already_running | |
end. | |
init() -> | |
Frequencies = {get_frequencies(), []}, | |
loop(Frequencies). | |
% Hard Coded | |
get_frequencies() -> [10,11,12,13,14,15]. | |
%% The Main Loop | |
loop(Frequencies) -> | |
receive | |
{request, Pid, allocate} -> | |
{NewFrequencies, Reply} = allocate(Frequencies, Pid), | |
Pid ! {reply, ?MODULE, Reply}, | |
loop(NewFrequencies); | |
{request, Pid , {deallocate, Freq}} -> | |
{NewFrequencies, Reply} = deallocate(Frequencies, Freq, Pid), | |
Pid ! {reply, ?MODULE, Reply}, | |
loop(NewFrequencies); | |
{request, Pid, stop} -> | |
Pid ! {reply, ?MODULE, stopped} | |
end. | |
%% Clears messages currently in the mailbox. | |
%% Only matches messages in {reply, frequency, ...} format to only | |
%% clear messages in the client process that were sent by the | |
%% frequency server. It should not clear other messages in the | |
%% client process. | |
clear() -> | |
receive | |
{reply, ?MODULE, Msg} -> | |
io:format("Clearing message ~w~n", [Msg]), | |
clear() | |
after 0 -> | |
ok | |
end. | |
%% The Internal Help Functions used to allocate and | |
%% deallocate frequencies. | |
%% Allocates frequency. | |
%% Only allows each client process to have one allocated frequency. | |
%% If a second allocation is attempted for a client process, an | |
%% error {error, already_has_allocation} is returned. | |
allocate({[], Allocated}, _Pid) -> | |
{{[], Allocated}, {error, no_frequency}}; | |
allocate({[Freq|Free], Allocated}, Pid) -> | |
AllocatedToPid = lists:filter(fun({_Freq, AllocatedPid}) -> AllocatedPid == Pid end, Allocated), | |
case length(AllocatedToPid) of | |
0 -> {{Free, [{Freq, Pid}|Allocated]}, {ok, Freq}}; | |
_ -> {{[Freq|Free], Allocated}, {error, already_has_allocation}} | |
end. | |
%% Deallocates a frequency | |
%% Only allows a process to deallocate a frequency | |
%% allocated to its own PID. | |
deallocate({Free, Allocated}, Freq, Pid) -> | |
case lists:member({Freq, Pid}, Allocated) of | |
true -> | |
NewAllocated = lists:delete({Freq, Pid}, Allocated), | |
{{[Freq|Free], NewAllocated}, ok}; | |
false -> | |
{{Free, Allocated}, {error, allocated_to_another_pid}} | |
end. | |
%% Functional interface | |
%% Allocates a new frequency. | |
%% Before allocating, clears old messages from previous attempts. | |
%% Note, this still potentially leaves the frequency client and server | |
%% in an inconsistent state if a frequency is allocated in the server, | |
%% but the client didn't get the response, and doesn't know what it is. | |
%% It then cannot allocate a new one, or deallocate its current one. | |
allocate() -> | |
clear(), | |
?MODULE ! {request, self(), allocate}, | |
receive | |
{reply, ?MODULE, Reply} -> Reply | |
after 5000 -> | |
{timeout, no_reply_from_server} | |
end. | |
%% Deallocates the given frequency. | |
deallocate(Freq) -> | |
clear(), | |
?MODULE ! {request, self(), {deallocate, Freq}}, | |
receive | |
{reply, ?MODULE, Reply} -> Reply | |
after 5000 -> | |
{timeout, no_reply_from_server} | |
end. |
This file contains hidden or 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_test). | |
-export([frequency_client/1]). | |
-import('frequency', [start/0]). | |
-include_lib("eunit/include/eunit.hrl"). | |
frequency_server_test_() -> | |
{setup, | |
fun stop_frequency_server/0, | |
fun() -> | |
{inorder, | |
[ | |
allocate_frequency(), | |
deallocate_frequency(), | |
cant_deallocate_frequency_not_owned() | |
]} | |
end}. | |
stop_frequency_server() -> | |
case whereis(frequency) of | |
undefined -> ok; | |
Pid -> | |
exit(Pid, kill), | |
timer:sleep(100) | |
end. | |
frequency_client(Func) -> | |
Self = self(), | |
spawn(fun() -> | |
Result = Func(), | |
Self ! Result | |
end), | |
receive | |
Msg -> Msg | |
end. | |
allocate_frequency() -> | |
stop_frequency_server(), | |
frequency:start(), | |
Result1 = frequency_client(fun() -> frequency:allocate() end), | |
?assertEqual({ok, 10}, Result1), | |
Result2 = frequency_client(fun() -> frequency:allocate() end), | |
?assertEqual({ok, 11}, Result2). | |
deallocate_frequency() -> | |
stop_frequency_server(), | |
frequency:start(), | |
?assertEqual(ok, frequency_client(fun() -> | |
frequency:allocate(), | |
frequency:deallocate(10) | |
end)), | |
?assertEqual({ok, 10}, frequency_client(fun() -> frequency:allocate() end)). | |
cant_deallocate_frequency_not_owned() -> | |
stop_frequency_server(), | |
frequency:start(), | |
Result1 = frequency_client(fun() -> frequency:allocate() end), | |
?assertEqual({ok, 10}, Result1), | |
Result2 = frequency_client(fun() -> frequency:deallocate(10) end), | |
?assertEqual({error, allocated_to_another_pid}, Result2). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment