Skip to content

Instantly share code, notes, and snippets.

@darcros
Created July 12, 2020 17:26
Show Gist options
  • Save darcros/ad91b294ca2dac602b7a13db5107d1c2 to your computer and use it in GitHub Desktop.
Save darcros/ad91b294ca2dac602b7a13db5107d1c2 to your computer and use it in GitHub Desktop.
Concurrent Programming in Erlang - The University of Kent - chapter 3.13
-module(frequency).
-export([init/1]).
-export([start_freq/1, stop_freq/1, allocate_freq/1, deallocate_freq/2]).
-define(TIMEOUT, 5000).
%% server implementation
allocate({[], Allocated}, _Pid) ->
{{[], Allocated}, {error, no_frequency}};
allocate({[Freq | Free], Allocated}, Pid) ->
{{Free, [{Freq, Pid} | Allocated]}, {ok, Freq}}.
deallocate({Free, Allocated}, {Freq, Pid}) ->
case lists:keyfind(Freq, 1, Allocated) of
{Freq, Pid} ->
NewAllocated = lists:delete({Freq, Pid}, Allocated),
{{[Freq | Free], NewAllocated}, ok};
{Freq, _} ->
{{Free, Allocated}, unauthorized};
false ->
{{Free, Allocated}, not_allocated}
end.
loop(Frequencies) ->
receive
{request, Pid, allocate} ->
{NewFrequencies, Reply} = allocate(Frequencies, Pid),
Pid ! {reply, Reply},
loop(NewFrequencies);
{request, Pid, {deallocate, Freq}} ->
{NewFrequencies, Reply} = deallocate(Frequencies, {Freq, Pid}),
Pid ! {reply, Reply},
loop(NewFrequencies);
{request, Pid, stop} ->
Pid ! {reply, stopped}
end.
init(InitialFreqs) -> loop({InitialFreqs, []}).
%% private functions
clear() ->
receive
_Msg -> clear()
after 0 ->
ok
end.
wait_reply() ->
receive
{reply, Rep} -> Rep
after ?TIMEOUT ->
clear(),
{error, timeout}
end.
%% API
start_freq(InitialFreqs) ->
spawn(frequency, init, [InitialFreqs]).
stop_freq(ServerPid) ->
ServerPid ! {request, self(), stop},
wait_reply().
allocate_freq(ServerPid) ->
ServerPid ! {request, self(), allocate},
wait_reply().
deallocate_freq(ServerPid, Frequency) ->
ServerPid ! {request, self(), {deallocate, Frequency}},
wait_reply().
-module(list_util).
-export([take/2]).
take(Pred, List) ->
take(Pred, List, []).
take(_Pred, [], _Acc) ->
false;
take(Pred, [Head | Tail], Acc) ->
case Pred(Head) of
true -> {Head, Acc ++ Tail};
false -> take(Pred, Tail, Acc ++ [Head])
end.
-module(router).
-export([init/1]).
-export([start_server/2, start/0, start/1, stop/0, allocate/0, deallocate/1]).
-define(TIMEOUT, 5000).
%% router implementation
allocate(Servers) ->
[{Server, Min, Max, Count} | Rest] = lists:keysort(4, Servers),
Reply = frequency:allocate_freq(Server),
case Reply of
{ok, _Freq} ->
NewServers = [{Server, Min, Max, Count + 1} | Rest],
{Reply, NewServers};
_ -> {Reply, Servers}
end.
deallocate(Servers, Freq) ->
Result = list_util:take(fun ({_Server, Min, Max, _Count}) ->
Freq >= Min andalso Freq =< Max end,
Servers),
case Result of
{{Server, Min, Max, Count}, Rest} ->
Reply = frequency:deallocate_freq(Server, Freq),
case Reply of
ok ->
NewServers = [{Server, Min, Max, Count - 1} | Rest],
{Reply, NewServers};
_ -> {Reply, Servers}
end;
_ ->
{{reply, server_not_found}, Servers}
end.
loop(Servers) ->
receive
{request, Pid, allocate} ->
{Reply, NewServers} = allocate(Servers),
Pid ! {reply, Reply},
loop(NewServers);
{request, Pid, {deallocate, Freq}} ->
{Reply, NewServers} = deallocate(Servers, Freq),
Pid ! {reply, Reply},
loop(NewServers);
{request, Pid, stop} ->
lists:foreach(fun ({Server, _Min, _Max, _Count}) ->
frequency:stop_freq(Server)
end,
Servers),
Pid ! {reply, ok}
end.
init(Servers) ->
Overlapping = [
{{Min1, Max1}, {Min2, Max2}} ||
{Server1, Min1, Max1, _Count1} <- Servers,
{Server2, Min2, Max2, _Count2} <- Servers,
Server1 /= Server2 andalso Min1 =< Max2 andalso Min2 =< Max1
],
case Overlapping of
[] -> {ok, loop(Servers)};
_ -> {error, {overlapping_ranges, Overlapping}}
end.
%% private functions
clear() ->
receive
_Msg -> clear()
after 0 ->
ok
end.
wait_reply() ->
receive
{reply, Rep} -> Rep
after ?TIMEOUT ->
clear(),
{error, timeout}
end.
%% API
start_server(Min, Max) ->
Server = frequency:start_freq(lists:seq(Min, Max)),
{Server, Min, Max, 0}.
start(Servers) ->
Router = spawn(router, init, [Servers]),
register(router, Router),
Router.
start() ->
start([
start_server(10, 15),
start_server(20, 25)
]).
stop() ->
router ! {request, self(), stop},
wait_reply().
allocate() ->
router ! {request, self(), allocate},
wait_reply().
deallocate(Frequency) ->
router ! {request, self(), {deallocate, Frequency}},
wait_reply().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment