Created
July 7, 2020 14:58
-
-
Save elbrujohalcon/42f1d7fe656c71a558d4d2a88439e634 to your computer and use it in GitHub Desktop.
Frequency with Errors
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
%% @doc Frequency server that "transmits" errors from server to client | |
-module frequency. | |
-export [test/0]. | |
-export [start/0, allocate/0, deallocate/1]. | |
-export [init/0]. | |
test() -> | |
%% Ensure the server is killed... This is ugly | |
catch exit(whereis(frequency), kill), | |
timer:sleep(100), | |
undefined = whereis(frequency), | |
%% If the server is not running, we get the proper error | |
try frequency:allocate() of | |
Result1 -> erlang:error({unexpected_result, Result1}) | |
catch | |
error:server_down -> ok | |
end, | |
%% Start the server and allocate a freq | |
frequency:start(), | |
Frequency = frequency:allocate(), | |
%% Try to deallocate another freq, we get an error | |
try frequency:deallocate(Frequency + 1) of | |
Result2 -> erlang:error({unexpected_result, Result2}) | |
catch | |
error:unallocated_frequency -> ok | |
end, | |
%% Allocate all the frequencies | |
[_|OtherFrequencies] = get_frequencies(), %% Cheating... I know ;) | |
ok = lists:foreach(fun(_) -> frequency:allocate() end, OtherFrequencies), | |
%% Try to allocate another frequency, get an error | |
try frequency:allocate() of | |
Result3 -> erlang:error({unexpected_result, Result3}) | |
catch | |
error:no_more_frequencies -> ok | |
end, | |
ok. | |
start() -> | |
register(frequency, spawn(frequency, init, [])). | |
allocate() -> | |
cmd(allocate). | |
deallocate(Freq) -> | |
cmd({deallocate, Freq}). | |
%% @doc This function encapsulates the logic to send commands to the server, | |
%% receive its result and "decode" it into the expected behavior. | |
cmd(Command) -> | |
try frequency ! {request, self(), Command} of | |
{request, _, Command} -> | |
receive | |
{reply, Result} -> Result; | |
{error, Error} -> erlang:error(Error); | |
{exit, Reason} -> erlang:exit(Reason) | |
after 5000 -> | |
erlang:error(timeout) | |
end | |
catch | |
error:badarg -> | |
erlang:error(server_down) | |
end. | |
init() -> | |
loop({get_frequencies(), []}). | |
get_frequencies() -> [10,11,12,13,14,15]. | |
loop(State) -> | |
receive | |
{request, From, Command} -> | |
try handle(Command, From, State) of | |
{Result, NewState} -> %% If a command produces a result | |
From ! {reply, Result}, %% send it back to the client as is | |
loop(NewState) | |
catch | |
throw:{Result, NewState} -> %% If a command returns early | |
From ! {reply, Result}, %% ALSO send the result to the client | |
loop(NewState); | |
Kind:Error:Stack -> %% If there is an error, we need to make THE CLIENT fail | |
From ! {Kind, Error}, %% NOT the server, so we "encode" the error | |
%% and we send it back to the client | |
io:format("Server ~p handling ~p: ~p~n\t~p~n", [Kind, Command, Error, Stack]), | |
loop(State) | |
end; | |
UnknownMessage -> | |
%% If we don't even know what process sent us this thing that we don't want | |
%% we can only log an error and hope for the best... | |
io:format("Unknown message received in Server: ~p~n", [UnknownMessage]), | |
loop(State) | |
end. | |
handle(allocate, _From, {[], _}) -> | |
erlang:error(no_more_frequencies); | |
handle(allocate, From, {[Freq|Freqs], Allocated}) -> | |
{Freq, {Freqs, [{Freq, From} | Allocated]}}; | |
handle({deallocate, Freq}, From, {Freqs, Allocated}) -> | |
case lists:keytake(Freq, 1, Allocated) of | |
{value, {Freq, From}, OtherAllocated} -> | |
{ok, {[Freq|Freqs], OtherAllocated}}; | |
_ -> | |
erlang:error(unallocated_frequency) | |
end. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment