Created
July 11, 2020 02:31
-
-
Save Xdeon/ddf681d75c74a8e4fc648f995fa27be3 to your computer and use it in GitHub Desktop.
frequency server with exceptions
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). | |
| -export([start/0, allocate/0, deallocate/1, stop/0, init/0]). | |
| start() -> | |
| Pid = spawn(?MODULE, init, []), | |
| register(?MODULE, Pid), | |
| Pid. | |
| allocate() -> | |
| send_receive(allocate). | |
| deallocate(Freq) -> | |
| send_receive({deallocate, Freq}). | |
| stop() -> | |
| send_receive(stop). | |
| send_receive(Info) -> | |
| try ?MODULE ! {request, self(), Info} of | |
| _ -> | |
| receive | |
| {reply, Reply} -> Reply; | |
| {error, Error} -> erlang:error(Error) | |
| after 500 -> | |
| erlang:error(timeout) | |
| end | |
| catch error:badarg -> | |
| erlang:error(serverdown) | |
| end. | |
| init() -> | |
| erlang:process_flag(trap_exit, true), | |
| Frequencies = {get_frequencies(), []}, | |
| loop(Frequencies). | |
| get_frequencies() -> | |
| [10, 11, 12, 13, 14, 15]. | |
| loop(Frequencies) -> | |
| receive | |
| Info -> | |
| case handle_info(Info, Frequencies) of | |
| stop -> ok; | |
| NewFrequencies -> loop(NewFrequencies) | |
| end | |
| end. | |
| handle_info({request, Pid, _}=Info, Frequencies) -> | |
| try handle(Info, Frequencies) of | |
| {reply, Reply, NewFrequencies} -> | |
| Pid ! {reply, Reply}, | |
| NewFrequencies; | |
| {stop, Reply, _} -> | |
| Pid ! {reply, Reply}, | |
| stop | |
| catch error:Error -> | |
| Pid ! {error, Error}, | |
| Frequencies | |
| end; | |
| handle_info({'EXIT', Pid, _Reason}, Frequencies) -> | |
| exited(Frequencies, Pid); | |
| handle_info(Unknown, Frequencies) -> | |
| io:format("received unknown message :~p~n", [Unknown]), | |
| Frequencies. | |
| handle({request, Pid, allocate}, Frequencies) -> | |
| {NewFrequencies, Reply} = allocate(Frequencies, Pid), | |
| {reply, Reply, NewFrequencies}; | |
| handle({request, _Pid, {deallocate, Freq}}, Frequencies) -> | |
| NewFrequencies = deallocate(Frequencies, Freq), | |
| {reply, ok, NewFrequencies}; | |
| handle({request, _Pid, stop}, Frequencies) -> | |
| {stop, stopped, Frequencies}. | |
| 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 -> | |
| erlang:error(unallocated_frequency) | |
| end. | |
| exited({Free, Allocated}, Pid) -> | |
| case lists:keytake(Pid, 2, Allocated) of | |
| {value, {Freq, Pid}, NewAllocated} -> | |
| {[Freq|Free], NewAllocated}; | |
| false -> | |
| {Free, Allocated} | |
| end. | |
| % tests | |
| -include_lib("eunit/include/eunit.hrl"). | |
| frequency_test() -> | |
| Server = start(), | |
| % test naming | |
| ?assertEqual(Server, whereis(?MODULE)), | |
| % test allocate | |
| ?assertEqual({ok, 10}, allocate()), | |
| % test duplicate allocate | |
| ?assertEqual({error, already_allocated}, allocate()), | |
| % test inproper deallocate | |
| ?assertMatch({'EXIT', {unallocated_frequency, _}}, catch deallocate(11)), | |
| ?assertEqual({error, already_allocated}, allocate()), | |
| % test proper deallocate | |
| ?assertEqual(ok, deallocate(10)), | |
| ?assertEqual({ok, 10}, allocate()), | |
| % test stop | |
| ?assertEqual(stopped, stop()), | |
| ok. | |
| serverdown_test() -> | |
| ?assertMatch({'EXIT', {serverdown, _}}, catch allocate()). | |
| client_fail_test() -> | |
| start(), | |
| Self = self(), | |
| spawn(fun() -> client(Self) end), | |
| % test allocate | |
| receive | |
| {reply, Reply} -> ?assertEqual({ok, 10}, Reply) | |
| end, | |
| timer:sleep(300), | |
| % client failed and allocated frequency should have been released | |
| ?assertEqual({ok, 10}, allocate()), | |
| stop(), | |
| ok. | |
| client(Pid) -> | |
| Reply = allocate(), | |
| Pid ! {reply, Reply}, | |
| timer:sleep(100), | |
| exit(bye). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is truly an excellent code. Kudos!