%% 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/1,allocate/0,deallocate/1,stop/0]). -export([test_non_overloaded_server/0, test_overloaded_server/0]). -export([init/1]). %% These are the start functions used to create and %% initialize the server. %% Additional WorkTime parameter allows to easily simulate overloaded server, %% It is passed to init and loop start(WorkTime) -> register(frequency, spawn(frequency, init, [WorkTime])). init(WorkTime) -> Frequencies = {get_frequencies(), []}, loop(Frequencies, WorkTime). % Hard Coded get_frequencies() -> [10,11,12,13,14,15]. %% The Main Loop loop(Frequencies, WorkTime) -> timer:sleep(WorkTime), receive {request, Pid, allocate} -> {NewFrequencies, Reply} = allocate(Frequencies, Pid), Pid ! {reply, Reply}, loop(NewFrequencies, WorkTime); {request, Pid , {deallocate, Freq}} -> NewFrequencies = deallocate(Frequencies, Freq), Pid ! {reply, ok}, loop(NewFrequencies, WorkTime); {request, Pid, stop} -> Pid ! {reply, stopped} end. %% Functional interface allocate() -> frequency ! {request, self(), allocate}, receive {reply, Reply} -> Reply after 10 -> {error, timeout} end. deallocate(Freq) -> frequency ! {request, self(), {deallocate, Freq}}, receive {reply, Reply} -> Reply after 10 -> {error, timeout} end. stop() -> frequency ! {request, self(), stop}, receive {reply, Reply} -> Reply after 10 -> {error, timeout} end. %% The Internal Help Functions used to allocate and %% deallocate frequencies. allocate({[], Allocated}, _Pid) -> {{[], Allocated}, {error, no_frequency}}; allocate({[Freq|Free], Allocated}, Pid) -> {{Free, [{Freq, Pid}|Allocated]}, {ok, Freq}}. deallocate({Free, Allocated}, Freq) -> NewAllocated=lists:keydelete(Freq, 1, Allocated), {[Freq|Free], NewAllocated}. clear() -> receive Msg -> io:format("~p~n", [Msg]), clear() after 0 -> ok end. %% tests test_non_overloaded_server() -> start(0), {ok, 10} = allocate(), stop(). % should return stopped test_overloaded_server() -> start(100), {error, timeout} = allocate(), timer:sleep(100), clear(), % should print {ok, 10} that came too late stop(). % should print {error, timeout}, because stop operation also takes too long.