Skip to content

Instantly share code, notes, and snippets.

@7-fl
Created May 2, 2017 18:41
Show Gist options
  • Save 7-fl/622c0fe4b36fb22d7487b1867f6ddfa3 to your computer and use it in GitHub Desktop.
Save 7-fl/622c0fe4b36fb22d7487b1867f6ddfa3 to your computer and use it in GitHub Desktop.
Alternating router -- multiple servers

fs.erl:

%% 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(fs).
-export([start/0,allocate/0,deallocate/1,stop/0]).
-export([server_init/1, router_init/1, router/2]).
-export([test/0, start_router/1]).
-export([client/2, test_init/0, test2/0]).

%%======== TEST ============

test_init() ->
    spawn(fs, test2, []),
    testing.

test2() ->
    Frequencies = [
        [10,11,12,13,14,15], %%The number of sublists in Frequencies
        [20,21,22,23,24,25], %%determines the number of servers that will
        [30,31,32],          %%be started.
        [40,41]
    ],
    start_router(Frequencies),  
                                                 
    timer:sleep(1000), %Make sure the servers start before 
                       %sending them requests.
    Clients =
        lists:map(fun(ClientArgs) ->
                          spawn(fs, client, ClientArgs)
                  end,
                  [[1,1000],[2,2000],[3,3000],[4,4000]] ), %%[Id,Sleep]
    io:format("Clients: ~w~n", [Clients]),
    timer:sleep(10000),
    stop().

test() ->
    io:format("Client is (~w)~n", [self()]),
    Frequencies = [
        [10,11,12,13,14,15],
        [20,21,22,23,24,25]
    ],
    start_router(Frequencies),  %%The number of sublists in Frequencies
                                %%determines the number of servers that will
                                %%be started.
    loop(14).

loop(0) ->
    done_looping;
loop(N) ->
    timer:sleep(1000),
    io:format("Client (~w) got msg: ~w~n", [self(), allocate()]),
    loop(N-1).

%%========== ROUTER ============

start_router(Frequencies) ->
    register(router, spawn(fs, router_init, [Frequencies])).
    
router_init(Frequencies) -> %Frequencies = [ [1,2,3], [10,11] ].
    Servers =  %% A server is a tuple: {ServerPid, Freqs}
        lists:map(fun(Freqs) ->
                      {
                          spawn(fs, server_init, [{Freqs, []}] ),
                          Freqs
                      }  %% => {ServerPid,Freqs}
                  end,
                  Frequencies), %% e.g. [ [10,11,12], [20,21,22,23] ] 
    io:format("Servers: ~lp~n", [Servers]),
    router(Servers, []).  %% [FreshServers, StaleServers] => All the servers start off as 'Fresh'.
    
router([], StaleServers) ->
    FreshServers = lists:reverse(StaleServers),  
    router(FreshServers, []);
router( [{FreshServer,_Freqs}=FreshServer_ | FreshServers ]=FreshServers_, StaleServers ) ->
    receive
        {request, Client, allocate}=Request ->
            FreshServer ! Request,
            io:format("router: sent allocate request to server (~w) from client (~w)~n", 
                      [FreshServer, Client]),
            router(FreshServers, [FreshServer_|StaleServers] );

        {request, Client, {deallocate,Freq}}=Request ->
            case find_server(Freq, FreshServers_, StaleServers) of
                false        ->
                    io:format("router: no servers allocated frequency ~w~n", [Freq]),
                    Client ! {reply, ok}; 
                TargetServer -> 
                    TargetServer ! Request,
                    io:format("router: sent {deallocate,~w} request to server (~w) from client (~w)~n", 
                              [Freq,TargetServer,Client])
            end,
            router(FreshServers_, StaleServers);    
   
        {request, Client, stop} ->
            stop_servers(FreshServers_, StaleServers),
            Client ! stopped
        end.

%%First search the FreshServers:
find_server(Freq, [], StaleServers) ->
    find_server(Freq, StaleServers);  %Then search the StaleServers.
find_server(Freq, [ {FreshServer,Freqs}|FreshServers ], StaleServers) ->
    case lists:member(Freq, Freqs) of
        false -> find_server(Freq, FreshServers, StaleServers);
        true  -> FreshServer
    end.

%%Search StaleServers:
find_server(_Freq, []) ->
    false;  %%Couldn't find a server that allocates Freq.
find_server(Freq, [ {StaleServer,Freqs}|StaleServers ] ) ->
    case lists:member(Freq, Freqs) of
        false  -> find_server(Freq, StaleServers);
        true   -> StaleServer
    end.
   
stop_servers(FreshServers, StaleServers) ->
    stop_servers(FreshServers),
    stop_servers(StaleServers).

stop_servers([]) ->
    servers_stopped;
stop_servers([ {ServerPid,_Freqs}=Server | Servers ]) ->
    ServerPid ! {request, self(), stop},
    receive 
        {reply, _Reply} -> ok
    end,
    io:format("router: sent stop message to server ~w~n", [Server]),
    stop_servers(Servers).
   
%%========= CLIENT ==========

client(Id, Sleep) ->
    handle_allocate_response(allocate(), Id, Sleep).

handle_allocate_response({ok, Freq}, Id, Sleep) ->
    io:format("client~w(~w): got frequency ~w~n", 
              [Id, self(), Freq]),

    timer:sleep(Sleep),
    deallocate(Freq),

    io:format("client~w(~w): called deallocate(~w)~n",
              [Id, self(), Freq]),

    client(Id, Sleep);
handle_allocate_response({error, no_frequency}, Id, Sleep) ->
    io:format("client~w(~w): no frequencies available~n",
              [Id, self()]),

    timer:sleep(500), %Wait small amount of time, then retry.
    client(Id, Sleep).

%%========  SERVER ================

start() ->
    register(fs,
	     spawn(fs, server_init, [])).

server_init(Freqs) ->
    %%Frequencies = {get_frequencies(), []},
    server_loop(Freqs).

%% Hard Coded
%get_frequencies() -> [10,11,12,13,14,15].

%% The Main Loop

server_loop(Freqs) ->
    receive
        {request, Pid, allocate} ->
            {NewFreqs, Reply} = allocate(Freqs, Pid),
            Pid ! {reply, Reply},
            io:format("server(~w): Sent allocate reply to client(~w)~n",[self(),Pid]),
            server_loop(NewFreqs);
        {request, Pid , {deallocate, Freq}} ->
            NewFreqs = deallocate(Freqs, Freq),
            Pid ! {reply, ok},
            server_loop(NewFreqs);
        {request, Pid, stop} ->
            Pid ! {reply, stopped};
        Other ->
            io:format("server(~w): got Other message ~w~n", [self(), Other])     
    end.

%% Functional interface

allocate() -> 
    router ! {request, self(), allocate},
    receive 
        {reply, Reply} -> Reply
    end.

deallocate(Freq) -> 
    router ! {request, self(), {deallocate, Freq}},
    receive 
        {reply, Reply} -> Reply
    end.

stop() -> 
    router ! {request, self(), stop},
    receive 
        {reply, Reply} -> Reply
    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}.

    

In the shell:

~/erlang_programs/fl_course2/3week/a6/fs$ ./run.sh
-----Now, do some Erlang for great Good!------

Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]

-----Now, do some Erlang for great Good!------

Servers: [{<0.33.0>,[10,11,12,13,14,15]},
          {<0.34.0>,[20,21,22,23,24,25]},
          {<0.35.0>,[30,31,32]},
          {<0.36.0>,[40,41]}]
Eshell V6.4  (abort with ^G)
1> Clients: [<0.39.0>,<0.40.0>,<0.41.0>,<0.42.0>]
router: sent allocate request to server (<0.33.0>) from client (<0.39.0>)
server(<0.33.0>): Sent allocate reply to client(<0.39.0>)
client1(<0.39.0>): got frequency 10
router: sent allocate request to server (<0.34.0>) from client (<0.40.0>)
server(<0.34.0>): Sent allocate reply to client(<0.40.0>)
router: sent allocate request to server (<0.35.0>) from client (<0.41.0>)
client2(<0.40.0>): got frequency 20
server(<0.35.0>): Sent allocate reply to client(<0.41.0>)
router: sent allocate request to server (<0.36.0>) from client (<0.42.0>)
client3(<0.41.0>): got frequency 30
server(<0.36.0>): Sent allocate reply to client(<0.42.0>)
client4(<0.42.0>): got frequency 40
router: sent {deallocate,10} request to server (<0.33.0>) from client (<0.39.0>) 
client1(<0.39.0>): called deallocate(10)
router: sent allocate request to server (<0.33.0>) from client (<0.39.0>)
server(<0.33.0>): Sent allocate reply to client(<0.39.0>)
client1(<0.39.0>): got frequency 10
router: sent {deallocate,20} request to server (<0.34.0>) from client (<0.40.0>) 
client2(<0.40.0>): called deallocate(20)
router: sent allocate request to server (<0.34.0>) from client (<0.40.0>)
server(<0.34.0>): Sent allocate reply to client(<0.40.0>)
client2(<0.40.0>): got frequency 20
router: sent {deallocate,10} request to server (<0.33.0>) from client (<0.39.0>) 
client1(<0.39.0>): called deallocate(10)
router: sent allocate request to server (<0.35.0>) from client (<0.39.0>)
server(<0.35.0>): Sent allocate reply to client(<0.39.0>)
client1(<0.39.0>): got frequency 31
router: sent {deallocate,30} request to server (<0.35.0>) from client (<0.41.0>) 
client3(<0.41.0>): called deallocate(30)
router: sent allocate request to server (<0.36.0>) from client (<0.41.0>)
server(<0.36.0>): Sent allocate reply to client(<0.41.0>)
client3(<0.41.0>): got frequency 41
router: sent {deallocate,31} request to server (<0.35.0>) from client (<0.39.0>) 
client1(<0.39.0>): called deallocate(31)
router: sent allocate request to server (<0.33.0>) from client (<0.39.0>)
server(<0.33.0>): Sent allocate reply to client(<0.39.0>)
client1(<0.39.0>): got frequency 10
router: sent {deallocate,40} request to server (<0.36.0>) from client (<0.42.0>) 
client4(<0.42.0>): called deallocate(40)
router: sent {deallocate,20} request to server (<0.34.0>) from client (<0.40.0>) 
router: sent allocate request to server (<0.34.0>) from client (<0.42.0>)
client2(<0.40.0>): called deallocate(20)
server(<0.34.0>): Sent allocate reply to client(<0.42.0>)
client4(<0.42.0>): got frequency 20
router: sent allocate request to server (<0.35.0>) from client (<0.40.0>)
server(<0.35.0>): Sent allocate reply to client(<0.40.0>)
client2(<0.40.0>): got frequency 31
router: sent {deallocate,10} request to server (<0.33.0>) from client (<0.39.0>) 
client1(<0.39.0>): called deallocate(10)
router: sent allocate request to server (<0.36.0>) from client (<0.39.0>)
server(<0.36.0>): Sent allocate reply to client(<0.39.0>)
client1(<0.39.0>): got frequency 40
router: sent {deallocate,40} request to server (<0.36.0>) from client (<0.39.0>) 
client1(<0.39.0>): called deallocate(40)
router: sent allocate request to server (<0.33.0>) from client (<0.39.0>)
server(<0.33.0>): Sent allocate reply to client(<0.39.0>)
client1(<0.39.0>): got frequency 10
router: sent {deallocate,41} request to server (<0.36.0>) from client (<0.41.0>) 
client3(<0.41.0>): called deallocate(41)
router: sent allocate request to server (<0.34.0>) from client (<0.41.0>)
server(<0.34.0>): Sent allocate reply to client(<0.41.0>)
client3(<0.41.0>): got frequency 21
router: sent {deallocate,31} request to server (<0.35.0>) from client (<0.40.0>) 
client2(<0.40.0>): called deallocate(31)
router: sent allocate request to server (<0.35.0>) from client (<0.40.0>)
server(<0.35.0>): Sent allocate reply to client(<0.40.0>)
client2(<0.40.0>): got frequency 31
router: sent {deallocate,10} request to server (<0.33.0>) from client (<0.39.0>) 
client1(<0.39.0>): called deallocate(10)
router: sent allocate request to server (<0.36.0>) from client (<0.39.0>)
server(<0.36.0>): Sent allocate reply to client(<0.39.0>)
client1(<0.39.0>): got frequency 41
router: sent {deallocate,41} request to server (<0.36.0>) from client (<0.39.0>) 
client1(<0.39.0>): called deallocate(41)
router: sent allocate request to server (<0.33.0>) from client (<0.39.0>)
server(<0.33.0>): Sent allocate reply to client(<0.39.0>)
client1(<0.39.0>): got frequency 10
router: sent {deallocate,20} request to server (<0.34.0>) from client (<0.42.0>) 
client4(<0.42.0>): called deallocate(20)
router: sent allocate request to server (<0.34.0>) from client (<0.42.0>)
server(<0.34.0>): Sent allocate reply to client(<0.42.0>)
client4(<0.42.0>): got frequency 20
router: sent {deallocate,31} request to server (<0.35.0>) from client (<0.40.0>) 
client2(<0.40.0>): called deallocate(31)
router: sent allocate request to server (<0.35.0>) from client (<0.40.0>)
server(<0.35.0>): Sent allocate reply to client(<0.40.0>)
client2(<0.40.0>): got frequency 31
router: sent {deallocate,10} request to server (<0.33.0>) from client (<0.39.0>) 
client1(<0.39.0>): called deallocate(10)
router: sent allocate request to server (<0.36.0>) from client (<0.39.0>)
server(<0.36.0>): Sent allocate reply to client(<0.39.0>)
client1(<0.39.0>): got frequency 41
router: sent {deallocate,21} request to server (<0.34.0>) from client (<0.41.0>) 
client3(<0.41.0>): called deallocate(21)
router: sent allocate request to server (<0.33.0>) from client (<0.41.0>)
server(<0.33.0>): Sent allocate reply to client(<0.41.0>)
client3(<0.41.0>): got frequency 10
router: sent {deallocate,41} request to server (<0.36.0>) from client (<0.39.0>) 
client1(<0.39.0>): called deallocate(41)
router: sent allocate request to server (<0.34.0>) from client (<0.39.0>)
server(<0.34.0>): Sent allocate reply to client(<0.39.0>)
client1(<0.39.0>): got frequency 21
router: sent stop message to server {<0.35.0>,[30,31,32]}
router: sent stop message to server {<0.36.0>,[40,41]}
router: sent stop message to server {<0.34.0>,[20,21,22,23,24,25]}
router: sent stop message to server {<0.33.0>,[10,11,12,13,14,15]}

=ERROR REPORT==== 2-May-2017::12:31:56 ===
Error in process <0.40.0> with exit value: {badarg,[{fs,deallocate,1,[{file,"fs.erl"},{line,203}]},{fs,handle_allocate_response,3,[{file,"fs.erl"},{line,149}]}]}


=ERROR REPORT==== 2-May-2017::12:31:56 ===
Error in process <0.39.0> with exit value: {badarg,[{fs,deallocate,1,[{file,"fs.erl"},{line,203}]},{fs,handle_allocate_response,3,[{file,"fs.erl"},{line,149}]}]}


=ERROR REPORT==== 2-May-2017::12:31:58 ===
Error in process <0.41.0> with exit value: {badarg,[{fs,deallocate,1,[{file,"fs.erl"},{line,203}]},{fs,handle_allocate_response,3,[{file,"fs.erl"},{line,149}]}]}


=ERROR REPORT==== 2-May-2017::12:31:58 ===
Error in process <0.42.0> with exit value: {badarg,[{fs,deallocate,1,[{file,"fs.erl"},{line,203}]},{fs,handle_allocate_response,3,[{file,"fs.erl"},{line,149}]}]}

i().
Pid                   Initial Call                          Heap     Reds Msgs
Registered            Current Function                     Stack              
<0.0.0>               otp_ring0:start/2                     1598     3478    0
init                  init:boot_loop/2                         4              
<0.2.0>               erlang:apply/2                        6772     2816    1
                      fs:stop/0                                7              
<0.3.0>               erlang:apply/2                        2586   141417    0
erl_prim_loader       erl_prim_loader:loop/3                   6              
<0.6.0>               gen_event:init_it/6                   2586     2655    0
error_logger          gen_event:fetch_msg/5                    8              
<0.7.0>               erlang:apply/2                        1598      470    0
application_controlle gen_server:loop/6                        7              
<0.9.0>               application_master:init/4              376       44    0
                      application_master:main_loop/2           6              
<0.10.0>              application_master:start_it/4          233       69    0
                      application_master:loop_it/4             5              
<0.11.0>              supervisor:kernel/1                   2586    45786    0
kernel_sup            gen_server:loop/6                        9              
<0.12.0>              rpc:init/1                             233       35    0
rex                   gen_server:loop/6                        9              
<0.13.0>              global:init/1                          233       52    0
global_name_server    gen_server:loop/6                        9              
<0.14.0>              erlang:apply/2                         233       19    0
                      global:loop_the_locker/1                 5              
<0.15.0>              erlang:apply/2                         233        3    0
                      global:loop_the_registrar/0              2              
<0.16.0>              inet_db:init/1                         233      251    0
inet_db               gen_server:loop/6                        9              
<0.17.0>              global_group:init/1                    233       59    0
global_group          gen_server:loop/6                        9              
<0.18.0>              file_server:init/1                     233       92    0
file_server_2         gen_server:loop/6                        9              
<0.19.0>              erlang:apply/2                        6772    93391    0
code_server           code_server:loop/1                       3              
<0.20.0>              supervisor_bridge:standard_error/      233       41    0
standard_error_sup    gen_server:loop/6                        9              
<0.21.0>              erlang:apply/2                         233        9    0
standard_error        standard_error:server_loop/1             2              
<0.22.0>              supervisor_bridge:user_sup/1           233       60    0
                      gen_server:loop/6                        9              
<0.23.0>              user_drv:server/2                      987     4501    0
user_drv              user_drv:server_loop/5                   8              
<0.24.0>              group:server/3                        2586    23953    0
user                  group:server_loop/3                      4              
<0.25.0>              group:server/3                        1598    11715    0
                      group:server_loop/3                      4              
<0.26.0>              erlang:apply/2                       17731     3764    0
                      shell:shell_rep/4                       17              
<0.27.0>              kernel_config:init/1                   233      286    0
                      gen_server:loop/6                        9              
<0.28.0>              supervisor:kernel/1                    233       58    0
kernel_safe_sup       gen_server:loop/6                        9              
<0.37.0>              erlang:apply/2                        1598    18430    0
                      c:pinfo/1                               50              
Total                                                      52403   353454    1
                                                             228              
ok

2> 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment