Archive

Posts Tagged ‘gen_server’

How to handle configuration in init/1 function without slowing down your erlang supervisor startup

April 17, 2013 1 comment

Many times if you work with Erlang and follow the OTP design principles in your implementation, you may end up having one or more supervisors spawning a set of processes that can be either other supervisors or workers.

Most likely the child processes implementing the workers will be based on gen_server behaviour and whether you’re working on your small side project or in some big company project they will need some sort of initialization during the start-up phase: in fact creating an ets or mnesia table, reading a configuration file or accepting connections on a socket are pretty common operations that you want to be executed before the worker handles  other messages and executes the operations connected to such messages.

According to the relative documentation, the gen_server process calls Module:init/1 to initialize and therefore the first strategy you may think to employ consists in doing the operations listed above within this function. What I mean here is something like:

start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

init([]) ->
    %% Some configuration operation here (e.g. handle ets/mnesia table)
    {ok, #state{}}.

This kind of approach is pretty common when the operations we want to take during initialization are cheap in terms of time, but what happens if the initialisation is expected to take a long time?

Suppose you have a supervisor that spawns many children and each child has some long time taking configuration. The supervisor will probably call the function start_link/3,4 of each child in sequence and will not be able to return until Module:init/1 of the child it is starting has returned.  This means that the supervisor won’t be able to start the next children on the fly and this will somehow slow down the whole supervisor startup phase. 

How can we solve this issue? Well, there are a couple of different ways to do it, but all of them are based on splitting the gen_server initialisation into two phases, a first phase implemented in the init/1 function during which we trigger some internal message for a future configuration and that returns immediately to the supervisor and  a second phase in which the configuration  actually takes place. In such a way we can free the supervisor startup from the time burden of all the children configurations.

Let’s see with some code what are the most common ways to achieve this results. My favourite technique consists into triggering the future configuration using a gen_server cast inside the init/1 function as follows:

start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

init([]) ->
    gen_server:cast(self(), startup),
    {ok, state{}}.

As you can see within the init/1 function we trigger a cast message to our process and immediately return. At this point we just need to handle the cast in the function handle_cast/2 and perform the needed configuration. This can be done in this way:

handle_cast(startup, State) ->
    %% Do your configuration here
    {noreply, State}.

A different way to achieve the same “two phases” result can be implemented as follows:

start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

init([]) ->
    self() ! startup,
    {ok, #state{}}.

This time we first send the atom ‘startup’ to the gen_server and then we return. Of course we need to handle that message within our gen_server as follows:

handle_info(startup, State) ->
    %% Do your configuration here
    {noreply, State}.

As you can see the logic is pretty much the same here so I won’t go into further details. 

The last way to achieve our result can be implemented by taking advantage of a timeout message. In practice in the init/1 function, instead of returning the tuple {ok, #state{}} we return the tuple {ok, #state{}, Timeout}. By including the value Timeout in the last tuple we specify that a ‘timeout’ atom will be sent to our gen_server unless a request or a message is received within Timeout milliseconds.

The ‘timeout’ atom should be handled by the handle_info/2 callback function. By setting the value of Timeout to 0 and adapting handle_info we can implement once again in an easy way our “two phases” configuration. Let’s see how this can be obtained:

start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

init([]) ->
    {ok, #state{}, 0}.

And the ‘timeout’ message can be handled as:

handle_info(timeout, State) ->
    %% Do your configuration here
    {noreply, State}.

Personally I don’t like the last approach because the atom ‘timeout’ is not so meaningful and it can lead to some misunderstanding. By the way the real problem here is that this approach is implemented taking advantage of an internal timer that should be evaluted: we can’t be sure that the message will be sent immediately, we just know that the message will be sent after at least 0 milliseconds.

Some reader here may say that this “two phases” approach is risky, because no one assures us that the configuration message will be the first message handled either in handl_cast/2 or handle_info/2.  Actually this is not completely true.

We can’t be sure 100% that a message sent in init/1 using either a cast of the operator ! will be the first one in the process queue of our gen_server, but considering that start_link/3,4 is synchronous there are really few chances for another process to send a message to our gen_server before we send the configuration message.

Final consideration: there is a more elegant way to achieve the same result that consists in the combination of the functions start_link/3 and init_ack/1 of the module proc_lib. For those of you interested in the topic I suggest the user guide of ranch.

Ideas for a gather spot in Erlang

April 2, 2012 1 comment

The game my flatmates and I play the most is for sure Monster Hunter Tri for WII. In this game you control an hunter which goes around, kills/captures monsters and so on. In the world of Monster Hunter is quite common to find (as in any other good game) some sort of “gather spot”, which are nothing more than places where you can find herbs to be picked up or rocks to be mined. A couple of days ago, while my friends where playing I started emacs and I started implementing a super simple gather spot.

I decided that my gather spot should respect these rules:

  • gather spot should be a non registered process
  • gather spot should be a gen_server process
  • gather spot can handle messages from players asking for a drop (if you don’t know what a drop is check out this)
  • gather spot should have different drops according to its type (e.g. rock/herb)
  • gather spot should have a random number of drops
  • gather spot should have different probabilities of drop (a friend of mine lost his mind trying to get “Eternal strife” rust shards)
  • gather spot should stop when it has nothing more to drop
In this post I will neither tell you how I managed the location of the gather spot in the XY-plane, nor how I configured a supervision tree for them; I want only to show you how simple is to implement something working in Erlang. You may notice that in my code I didn’t register with a name the process, this is due to the fact that I may have a huge number of this kind of process in a single Erlang node. Here follow resource.erl and helper.hrl…I will not give you explanation about these two files since they are quite simple. 
%%%-------------------------------------------------------------------
%%% @author Paolo <>
%%% @copyright (C) 2012, Paolo
%%% @doc
%%%
%%% @end
%%% Created : 13 Mar 2012 by Paolo
%%%-------------------------------------------------------------------
-module(resource).

-behaviour(gen_server).

%% API
-export([start_link/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
 terminate/2, code_change/3]).

-define(SERVER, ?MODULE).
-include("helper.hrl").

%%%===================================================================
%%% API
%%%===================================================================

%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
 gen_server:start_link(?MODULE, [], []).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% @end
%%--------------------------------------------------------------------
init([]) ->
 <<A:32, B:32, C:32>> = crypto:rand_bytes(12),
 random:seed({A,B,C}),
 Rs = resources(),
 Rt = lists:nth(random:uniform(length(resources())), Rs),
 PossibleDrops = possible_drops(Rt),
 Drops = [generate_drops(PossibleDrops, random:uniform(100)) ||
           _  <- lists:seq(1, number_of_drops(resource_max_drops))],
 {ok, #state{drops = Drops}}.

%% {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_call(drop, _From, #state_resource{drops = [LastDrop | []]} = State) ->
 {stop, normal, LastDrop, State};

handle_call(drop, _From, #state_resource{drops = [Drop | Drops]} = State) ->
 {reply, Drop, State#state_resource{drops = Drops}};

handle_call(_Request, _From, State) ->
 Reply = ok,
 {reply, Reply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_cast(stop, State) ->
 {stop, normal, State};

handle_cast(_Msg, State) ->
 {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling all non call/cast messages
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
 {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_server terminates
%% with Reason. The return value is ignored.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
 ok.

code_change(_OldVsn, State, _Extra) ->
 {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================

As you can see, the code above is fairly simple and respect all the rules I introduced above. You may notice that resource type, drops number and actual drop are random. The important thing here is that the gen_server which represents the resource will exit in normal way when no more drops are present in its state.  Let’s see helper.hrl, which will give you some better insight:

-record(state_resource, {pid,
			 drops = []}).

resources() ->
    [rock, plant].

possible_drops(rock) ->
    ["Rust Shard",
     "Charm",
     "Iron",
     "Whetstone",
     "Stone"
    ];

possible_drops(herb) ->
    ["Some rare stuff",
     "Some uncommon stuff",
     "Ivy",
     "Spider Web",
     "Herb"
    ];

generate_drops(Drops, Number) when Number < 3 ->
    lists:nth(1, Drops);

generate_drops(Drops,Number) when Number < 14 ->
    lists:nth(2, Drops);

generate_drops(Drops, Number) when Number < 30->
    lists:nth(3, Drops);

generate_drops(Drops, Number) when Number < 50 ->
    lists:nth(4, Drops);

generate_drops(Drops, _Number) ->
    lists:nth(5, Drops).

number_of_drops(resource_max_drops) ->
    {ok, MaxDrops} = application:get_env(resource_max_drops),
    number_of_drops(MaxDrops);

number_of_drops(MaxDrops) when is_integer(MaxDrops)->
    <<A:32, B:32, C:32>> = crypto:rand_bytes(12),
    random:seed({A,B,C}),
    random:uniform(MaxDrops).

Now, some may argue that this code is far from perfect…well I agree. In fact this is just a starting point…a lot of stuff can be improved, therefore why don’t you start from this code and do something on you own? How can you improve this code? Here some ideas:

  • how would you place in a random way a resource spot in a given x-y or x-y-x point? 
  • how would you handle supersion tree? 
  • how would you handle resource respawning?
  • how would you abstract helper.hrl?
Categories: English, Erlang Tags: , ,

Supervisors in Erlang OTP

January 28, 2010 2 comments

A supervisor is an OTP behaviour (a process design pattern) made available in a set of library modules that come with the normal Erlang distribution. In practice a supervisor is a piece of code, done by expert developers, which simplyfies our life!
Basically OTP provides two different kinds of processes: workers (e.g. gen_servers or gen_fsm) that as you can easily understand do the actual job and supervisors which monitor workers’s (or other supervisors’s) statuses.

In the figure above, square boxes represents supervisors and circles represent workers.

A supervisor’s task is not only to check whether its childrens are up and running or not, it also may take actions in one of these cases (e.g. restart a child if it is down).

As always let’s introduce some code and let’s see how the things work!

-module(my_supervisor).
-behaviour(supervisor).

%% API
-export([start_link/0]).

%% Supervisor callbacks
-export([init/1]).
-define(SERVER, ?MODULE).

%%====================================================================
%% API functions
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the supervisor
%%--------------------------------------------------------------------
start_link() ->
  supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%%====================================================================
%% Supervisor callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Func: init(Args) -> {ok,  {SupFlags,  [ChildSpec]}} |
%%                     ignore                          |
%%                     {error, Reason}
%% Description: Whenever a supervisor is started using
%% supervisor:start_link/[2,3], this function is called by the new process
%% to find out about restart strategy, maximum restart frequency and child
%% specifications.
%%--------------------------------------------------------------------
init([]) ->
  Child = {mychild, {mychild, start_link, []},
           permanent, 2000, worker, [mychild]},
  {ok, {{one_for_all, 1, 1}, [Child]}}.

The supervisor is started by the function start_link/0, which call the init/1 function.

As you can see we declared a variable named Child which represents the specification of a child for which the supervisor is responsible.

The variable is in the form: {Id, StartFunc, Restart, Shutdown, Type, Modules}

Id : is the name used to identify the child specification internally by the supervisor

StartFunc : represents the function used to start the child process. It is a module-function-arguments tuple used as apply(M, F, A)

Restart : defines when a terminated child process should be restarted. It can be one between:

  1. permanent : in this case child process is always restarted
  2. transient : in this case child process is restarted only if it terminates abnormally, i.e. with another exit reason than normal
  3. temporary : in this case child process is never restarted

Shutdown : defines how a child process should be terminated

  1. brutal_kill : in this case child process is unconditionally terminated using exit(Child, kill)
  2. integer value : in this case the supervisor tells the child process to terminate by calling exit(Child, shutdown) and then waits for an exit signal back. If no exit signal is received within the specified time, the child process is unconditionally terminated using exit(Child, kill)
  3. infinity : in this case child process is another supervisor, shutdown should be set to infinity to give the subtree enough time to shutdown

Type : it specifies if the child process is a supervisor or a worker (can be supervisor/worker)

Modules : this should be a list with one element [Module], where Module is the name of the callback module, if the child process is a supervisor, gen_server or gen_fsm. If the child process is a gen_event, Modules should be dynamic

The last tuple is in the form: {ok, {{RestartStrategy, MaxR, MaxT}, [Child]}}

RestartStrategy : indicates how to handle process restart. Can be one of the following:

  1. one_for_one : if the corresponding child process terminates, only that process is restarted
  2. one_for_all : if the child process terminates, all other child processes are terminated and then all child processes, including the terminated one, are restarted
  3. rest_for_one : if the child process terminates, the ‘rest’ of the child processes (i.e. the child processes after the terminated process in start order ) are terminated. Then the terminated child process and the rest of the child processes are restarted

MaxR and MaxT are related: if more than MaxR number of restarts occur in the last MaxT seconds, then the supervisor terminates all the child processes and then itself. When the supervisor terminates, then the next higher level supervisor takes some action. It either restarts the terminated supervisor, or terminates itself.

As you can see the only element inside the list is Child that contains the child specifications we set before; obviously you can add to this list more than one child specification.

Let’s test our supervisor. The first thing that I will do is coding a simple gen_server that prints a message anytime it is started:

-module(mychild).
-behaviour(gen_server).

%% API
-export([start_link/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	 terminate/2, code_change/3]).

-record(state, {}).

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
    io:format("supervisor started me!~n", []),
    {ok, #state{}}.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
    ok.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

Now let’s test it in an Erlang shell:

bellerofonte@pegaso:~/Desktop$ erl
Eshell V5.7.3  (abort with ^G)
1> l(mysupervisor).
{module,mysupervisor}
2> mysupervisor:start_link().
supervisor started me!
{ok,<0.35.0>}
3> whereis(mychild).
<0.36.0>
4> erlang:exit(whereis(mychild), kill).
true
supervisor started me!
5> whereis(mychild).
<0.39.0>

As you can see as we start the supervisor, the child is started as well with the pid <0.36.0>.

After that we kill that child process using the Erlang BIF exit/2; at that point the supervisor restarts the child and a following whereis/1 command shows that the process has been restarted with a new pid <0.39.0>.

A lot of more information about this topic may be found on the official page of Erlang otp design principles!

REMOTE PROCEDURE CALLs: C vs. Erlang (part 2)

December 21, 2009 Leave a comment

In the previous post, I have told you how to make a counter server in C and then call its functionalities through RPC calls.

In Erlang it is defined a module called rpc. It provides services which are similar to remote procedure calls. It also contains broadcast facilities and parallel evaluators. As I told you also in the previous blog a remote procedure call is a method to call a function on a remote node and collect the answer.

The most important function in this module is rpc:call(Node, Module, Function, Arguments), it executes the specific function defined in the specific module, with the specified arguments on the node Node and returns the corresponding that can be either the return value, or a tupla of the form {badrpc, Reason}.

Sounds easy! Thus let’s start by coding our counter server:

%%%-------------------------------------------------------------------
%%% File    : counter_svr.erl
%%% Author  : XXXXX
%%% Description : A counter server
%%%
%%% Created : 19 Dec 2009 by XXXXX
%%%-------------------------------------------------------------------
-module(counter_svr).

-behaviour(gen_server).

%% API
-export([start_link/0,count/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	 terminate/2, code_change/3]).

-record(state, {counter=0}).

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
    {ok, #state{}}.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(count, _From, State) ->
    Reply = State#state.counter + 1,
    NewState = #state{counter=Reply},
    {reply, Reply, NewState};

handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
    ok.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
count() ->
    gen_server:call(?MODULE, count).

As you can see I used the template of a gen_server for my implementation of the counter server. The value of the counter is stored inside its state, and it starts with the value of 0. The server handles a specific call that is aimed to retrieve the counter from the server, increment it by one and return that value to the client.  In the internal functions space, I defined a function named count that performs the specific call to the gen_server.

Easy, isn’t it?

Now we have only to start the server in a Erlang node (we name it server@ourlocalhost).

bellerofonte@pegaso:~$ erl -sname server@pegaso
Erlang R13B02 (erts-5.7.3)
Eshell V5.7.3  (abort with ^G)
(server@pegaso)1> counter_svr:start_link().
{ok,<0.40.0>}

At this point the server is up an running on our Erlang node, so the only thing left is to start another Erlang node (or many nodes) and perform some RPC from these. First of all we start the second node, you should know that this node must be connected to the previous one and share with it the same cookie, if you don’t see Erlang User Guide for more info. In brief I can tell you that you can check whether two nodes are connected by calling in one node the function net:ping(‘othernode@host’) that gives back a ‘pong’ if they are connected, or a ‘pang’ if they are not…..moreover you can check if the nodes have the same cookie by calling in both nodes the function erlang:get_cookie() (otherwise you can set the cookie with erlang:set_cookie(Cookie). Coming back to our discussion on rpc, inside the second node we call the function through RPC by using as stated above rpc:call(Node, Module, Function, Arguments)…in our specific case Node is the node where the server is running, Module is the module implementing the server, Function is the function that retrieves the counter and increments it and Arguments is an empty list, since the function takes no arguments.

bellerofonte@pegaso:~$ erl -sname client@pegaso
Erlang R13B02 (erts-5.7.3)
Eshell V5.7.3  (abort with ^G)
(client@pegaso)1> rpc:call('server@pegaso', counter_svr, count, []).
1
(client@pegaso)2> rpc:call('server@pegaso', counter_svr, count, []).
2
(client@pegaso)3> rpc:call('server@pegaso', counter_svr, count, []).
3
(client@pegaso)4> rpc:call('server@pegaso', counter_svr, count, []).
4

As you may see in the example I posted above, multiple RPCs to our server, give back the result expected…..

In my opinion this comparison explains why I still prefer using Erlang when talking or coding distributed systems!

Categories: C, English, Erlang Tags: , ,

Serving multiple clients on the same port in Erlang

December 15, 2009 Leave a comment

In the previous post, I wrote down a really basic interface for the gen_server I’m going to use in my card game: there were a couple of basic handle_calls used to manage users’ turns etc etc.

Obviously, if we want to handle connecting users, we must set up a socket on a specific port and listen to what is coming in there.

A really easy way to do this is shown in this post. Let’s start by writing in our gen_server (in the internal functions space) the following function:

conn_manager(Port) ->
    {ok, Listen} = gen_tcp:listen(Port, [binary,
                                                    {keepalive, true},
                                                    {reuseaddr, true},
                                                    {active, once}]),
    spawn(fun() -> parallel_connection(Listen) end).

conn_manager(Port) is a really simple fuction called in the init function; it does the following: create a tcp socket on the Port passed as argument to the function, this socket will have some features, as you can see we specified that the traffic going trough it is of type binary, that we want the server to send keepalive packets to mantain the connection up and running, that the address may be reused and most importan that this socket will be active once.

What does the active once mean? Well, the socket could have been always active, and this means that we would have been listening to the incoming data without any restrictions…well, sometimes this is a good thing, but think about Denial of Service attacks..we definitely may prefer to listen to the incoming traffic only when neededt…

After the creation of the socket we spawn a new process and pass to it the reference to the socket.

Let’s move on:

parallel_connection(Listen) ->
    {ok, Socket} = gen_tcp:accept(Listen),
    case is_in_blacklist(Socket) of
	false ->
	    spawn(fun() -> parallel_connection(Listen) end),
	    conn_loop(Socket);
	true ->
	    gen_tcp:close(Socket)
    end.

parallel_connection(Listen) is the function spawned as a new process by the previous function. In the very beginning it accepts any incoming connection and checks whether it comes from a blacklisted IP (note that is_in_blacklist is function that I did by myself so it is not a erlang standard function, if the clients in in the blacklist the connection is closed, otherwise we do two things: first we spawn a new parallel_connection function so that new clients may be able to connect to the server, and the we call the function conn_loop(Socket) so that we can manage the traffic coming from the already connected user.

conn_loop(Socket) ->
    receive
	{tcp, Socket, Bin} ->
	    inet:setopts(Socket, [{active, once}]),
	    case Bin of
                 ....case matching with incoming binaries (e.g. join match)
	    end,
	    conn_loop(Socket);
	{tcp_closed, Socket} ->
	    closed
    end.

As you can see conn_loop(Socket) is a very basic function…it is a simple receive function that in case of incoming traffic {tcp, Socket, Bin} triggers the socket to receive once more and than with a case construct manages the incoming binaries.

This post was written in a hurry, so if you have questions or comments or anything just comment it or mail me so that I can correct it!

Categories: English, Erlang Tags: , ,

A card game in erlang…continue!

December 9, 2009 2 comments

Here I am with a new post!

A couple of days ago, I had some time to fix my erlang environment in my new Ubuntu Karmic Koala, so I decided to continue with the development of my cards game in erlang.

If you remember from my previous posts, I did a process for handling the table, it was a gen_server providing a basic interface for a set of actions users may want to perform, as “play a card on the table”, “take a single card/a set of cards from table”, “give me 10 cards” and so on…

This process was also responsible for checking whether the action user wants to do is allowed (e.g. is the card user wants to take in the table), so I thought that next step would have been the creation of another gen_server.

In my mind this server should have listened to a specific port waiting for users; since “scopa” is a game played by 4 users, the server should have waited until 4 users joined the game, than it would have started a new table process for these users.

Now I’m going to write some code, and discuss it with you.

Let’s start with some handle_calls for our gen_server:

handle_call({add, Socket}, _From, State) ->
    NewSockets = [Socket | State#state.sockets],
    Reply = ok,
    NewState = #state{sockets=NewSockets},
    {reply, Reply, NewState};

The previous handle_call adds a new socket to the list of sockets stored in the state of the gen_server; I use the sockets to distinguish each player.

handle_call(get_players_number, _From, State) ->
    PlayersNum = length(State#state.sockets),
    Reply = PlayersNum,
    {reply, Reply, State};

The previous handle_call is used to retrieve the number of users who joined already the game.

handle_call(get_players_sockets, _From, State) ->
    PlayersSockets = State#state.sockets,
    Reply = PlayersSockets,
    {reply, Reply, State};

The previous handle_call is used to retrieve the list of sockets in the game (we can use this to understand whether the socket sending commands is a valid user)

handle_call(get_player_turn, _From, State) ->
    [PlayerSocket | _Others] = State#state.sockets,
    Reply = PlayerSocket,
    {reply, Reply, State};

The previous handle_call is used to retrieve wich user/socket is the next to perform an action (so we can manage turns)

handle_call(update_turn, _From, State) ->
    [PlayerSocket | Others] = State#state.sockets,
    NewSockets = Others ++ [PlayerSocket],
    NewState = #state{sockets=NewSockets},
    Reply = ok,
    {reply, Reply, NewState}

The last handle_call is used to update the turn list after a player submit an action.

Let’s say that a player sends a command to our server…before forwarding this command to the table we must check whether the user who sent the command is a player of the match and whether he is the player we expect to play (turn); we may want to use two functions like:

%%--------------------------------------------------------------------
%% Func: is_player(Socket) -> true | false
%% Description: Given a socket checks whether it represents a user in
%%              current match
%%--------------------------------------------------------------------
is_player(Socket) ->
    lists:member(Socket, gen_server:call(?MODULE, get_players_sockets)).

and

%%--------------------------------------------------------------------
%% Func: is_player_turn(Socket) -> true | false
%% Description: Given a socket checks whether the user it represents
%%              is the one expected to play
%%--------------------------------------------------------------------
is_player_turn(Socket) ->
    Socket == gen_server:call(?MODULE, get_player_turn).

Next time new stuff! Stay tuned!

Categories: English, Erlang Tags: , , , ,

Ping – Pong example in Erlang 2

September 14, 2009 Leave a comment

Eccomi di nuovo qui, per parlarvi del mio linguaggio preferito!

L’ultima volta mi sono interrotto bruscamente a causa della lunghezza del post che stava diventando insostenibile pure per me..figuariamoci per un povero lettore poi!

Se non sbaglio si stava parlando di server, client e di ping pong; dunque quest’oggi non farò altro che proseguire con l’argomento, introducendo una seconda possibile implementazione basata sul gen_server behaviour offerto da erlang.

Il behaviour module gen_server è un utile punto di partenza per implementare un server; un generico processo (gen_server) implementato usando questo behaviour avrà un set standard di funzioni d’interfaccia e fornirà funzionalità per evidenziare e riportare errori. Inoltre è facile inserire tale tipo di modulo in un OTP supervision tree.

Un modulo implementato su un gen_server behaviour mette a disposizione una serie di cosidette callbacks functions, che aiutano lo sviluppatore nel corso dello sviluppo e che gli permettono di scrivere molto meno codice.

Possiamo pensare che dietro tale behaviour ci sia una funzione di loop ad un parametro (detto state). Tale funzione loop/1 è invisibile all’utente, che ha però a disposizione delle funzionalità per accedere a tale stato (ottenendone il valore attuale, o modificandolo).

Se come me utilizzate emacs con l’erlang mode, potete ottenere una versione molto semplice di gen_server semplicemente andando all voce tools -> skeletons -> gen_server nel menù a tendina, vi appariranno sul vostro buffer tutte le funzioni base con rispettivi commenti che io ho tolto per non appesantire il codice. Piu o meno avrete qualcosa di simile:

-module(server).

-behaviour(gen_server).

%% API
-export([start_link/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	 terminate/2, code_change/3]).

-record(state, {}).

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

init([]) ->
    {ok, #state{}}.

handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

star_link/0 è la funzione che fa partire il processo e che lo registra (se specificate un nome). Tale funzione andrebbe sempre chiamata dal supervisor, ed a sua volta chiama la funzione init del modulo (potete specificare dei parametri se la vostra init li necessita).

La funzione init/0 nel nostro caso viene chiamata da start_link e se il processo è stato inizializzato nel modo corretto ritorna la tupla {ok, #state} dove il secondo elemento rappresenta lo stato interno del processo. In realtà in init andrebbero eseguite tutte le operazioni di inizializzazione del processo (ad esempio se intendeve creare delle tables in mnesia dovreste farlo qui). La funzione terminate/2 rappresenta l’opposto di init: tale funzione viene chiamata ogni qualvolta il gen_server sta per terminare appunto e al suo interno dovrebbero essere specificate tutte le funzioni rivolte al clean-up. In alcuni casi terminate tuttavia non viene chiamata, vi suggerisco di vedere questo link per maggiori informazioni a riguardo.

Le funzioni handle_call/3, handle_cast/2 ed handle_info/2 sono invece i veri e propri accessi allo state del nostro processo.  La prima si occupa di gestire le richieste al server in modo sincrono e fornisce come risultato una tupla di forma {reply, Reply, State} dove Reply è il valore spedito al richiedente e State è il nuovo state assunto dal loop interno. La seconda opera in modo simile, ma si occupa delle richieste asincrone, indipendentemente dal risultato della chiamata ritorna sempre ok. La teza invece viene invocata quando la richiesta non è nè di tipo sincrono nè asincrono oppure quando sorge un timeout error.

Per spiegarne il funzionamento passo ad implementare il nostro server di ping pong (implemento solo handle_call/3):

-module(server).

-behaviour(gen_server).

%% API
-export([start_link/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	 terminate/2, code_change/3]).

-record(state, {times}).

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

init([]) ->
    {ok, #state{times=0}}.

handle_call({ping, 0}, _From, _State) ->
    Reply = {pong, 0},
    NewState = #state{times = 0},
    {reply, Reply, NewState};

handle_call({ping, N}, _From, _State) ->
    Reply = {pong, N-1},
    NewState = #state{times = N-1},
    {reply, Reply, NewState};

handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

Come vedete ho iniziato cambiando il record su cui è basato lo status: in questo caso è semplicemente un valore detto times, quando il server viene inizializzato tale valore è a 0. Sono state aggiunte due nuove dichiarazioni per handle_call/3 (mi raccomando che quella generica va alla fine, state attenti pure ai punti e virgola!): la prima riguarda ogni richiesta sincrona rappresentata dalla tupla {ping, 0} mentre la seconda le richieste del tipo {ping, N}.

Ogniqualvolta arriva al server una chiamata del primo tipo (tramite la funzione gen_server:call(ServerRef, Request) (dove ServerRef è il riferimento al nostro server e Request la richiesta),n settiamo il valore di ritorno a {pong, 0} e cambiamo il valore dello stato interno a 0. Se arriva invece al server una chiamata di tipo {ping, N}, rispondiamo con un {ping, N-1} ed aggiorniamo il valore dello stato interno a N-1.

Il client è molto facile da implementare in questo caso. Ci serve una sola funzione ping/1 che esegue gen_server:call/2; ovviamente in questo caso il primo paramentro sarà il nome con cui è registrato il nostro server. Il risultato di tale chiamata verrà sottoposto ad un pattern matching tramite il quale la nuova operazione da compiere verrà stabilita.

-module(client).
-export([ping/1]).

ping(Times) ->
    case gen_server:call(server, {ping, Times}) of
	{pong, 0} ->
	    io:format("pong ~n", []);
	{pong, TimesLeft} ->
	    io:format("pong ~n", []),
	    ping(TimesLeft)
    end.

Ed ecco il codice in esecuzione:

bellerofonte:blog pegaso$ erl
Erlang (BEAM) emulator version 5.6.5  [async-threads:0]
[kernel-poll:false]

Eshell V5.6.5  (abort with ^G)
1> server:start_link().
{ok,<0.33.0>}
2> client:ping(2).
pong
pong
ok
3>
Categories: Erlang Tags: , ,