Archive

Posts Tagged ‘OTP’

My thoughts about: “Erlang by Example with Cesarini and Thompson”

June 14, 2013 1 comment

In my previous post about TDD and Erlang I listed some ways to improve both coding proficiency and Erlang knowledge.  This week I would like to write here my opinion about the series of videos gently provided to me by O’Reilly: “Erlang by Example with Cesarini and Thompson”

The series is composed by 8 chapters (even though I would prefer calling them “lectures”):

  • Introduction
  • Basic Erlang
  • Sequential Programming
  • Concurrent Programming I
  • Concurrent Programming II
  • Process Error Handling
  • Mobile Frequency Server I
  • Mobile Frequency Server II

I guess that many of you (especially the non Erlangers ones) are now wondering: “What are the topics in detail? Who is the target audience? Should I buy the videos instead of a normal book? Are these videos really so good?” Well, let me answer that in the rest of this post.

What are the topics in details? – As you may notice from the list above the video lectures starts with the basics (data types, variables, pattern matching, etc etc.). After that you will learn things mostly related to sequential programming and concurrency. A good point of these videos is that you will end up with something real: a simple client-server application handling mobile frequencies. Notice that in the list above there is no reference to OTP: in fact you won’t learn about OTP here, but I believe many Erlangers are right when they say: “Learn with ‘normal’ Erlang and code your application using OTP”.

Who is the target audience? – Good question. Are you new to Erlang development? If so buy these videos. You will learn much and in a fast way. On the other hand I think that many experienced Erlang developer should take a look at these lessons, not only to review the basic concepts but also to hear the considerations and the suggestions of two of the most respected Erlangers out there. Sure, if you know Erlang very well you will skip some stuff, but still you will enjoy the lectures in their whole.

Should I buy the videos instead of a normal book? – No. Don’t do it! I have to be clear here: you won’t learn Erlang just by watching these videos. I believe these videos must be considered as a wonderful integration of what you read on a real book. As I wrote above you will benefit from the talking between the authors, but I must say that nothing beats the good old detailed books (especially the paper ones). So my advice is to select one of the many Erlang books out there, read it and complete the study chapter by chapter using these videos.

Are the videos really so good? – Yes they are, both for content quality and video quality. I must admit that every time I see some content authored by Francesco Cesarini and Simon Thompson I feel at ease. My first Erlang Book ever was “Erlang Programming”  and since then Francesco and Simon never let me down. I believe this is mostly related to their great experience in teaching and consulting: they know what to say, when to say it and how to say it. The quality of the videos is great too! The videos are 1280 × 720 and last on average ~15 minutes and this time amount is perfect because you never got bored or tired while watching them. I would like to point out that I read in some reviews people complaining about codec problems, but before writing this post I tried them in Ubuntu, Mac, Windows 7 and iPad and didn’t notice any kind of problem.

That’s all folks! Now it’s up to you: are you going to buy these videos???

 

 

 

 

 

 

 

Advertisements

Erlang Camp 2013 is coming!

May 6, 2013 Leave a comment

Amsterdam: beautiful city of bicycles, canals and….. Erlang!

Nothing to do on Aug 30-31, 2013? What about  travelling to the lovely city of Amsterdam and attend the Erlang Camp 2013?

If you have been following my blog for a while you should already know what Erlang Camp is: an intensive two day learning experience focused on getting you up to speed on creating large scale, fault tolerant distributed applications in Erlang.

In particular, during the Erlang Camp 2013 which is exceptionally sponsored by the amazing company SpilGames you will get in touch with several Erlang topics as:

  • Erlang basic stuff
  • Erlang OTP
  • How to ship your Erlang code using applications and releases
  • Erlang Distribution

More information on the Erlang Camp schedule may be found in this web page.

Erlang Camp is a pretty good way to learn Erlang language and to get in touch with some of the best Erlang teachers and developers outh there. Knowing that only 100 seats are available and that they will go quickly I suggest you to hurry and register for the event!

 

My brief soliloquy on gen_fsm timeouts

June 21, 2012 1 comment

Have you ever tried to code some stuff using as stub a gen_fsm? Well, I know it is not the favourite OTP behaviour among Erlang developers , but still I have been playing with them a couple of times.

The first time I thought about the use of  a gen_fsm  was more than a year ago. For one of my embedded systems courses, my group was assigned a small project: this project consisted in the implementation of a small finite state machine which had the task to collect some data (e.g. temperature and humidity) and send these data to a central hub. There was also a constraint on time, so that the finite state machine had to move from state to state every 5 seconds no matter what.

My team mate and I, had to write the code in C, but at that time I didn’t know much about gen_fsm behaviour, so I implemented the same code in Erlang in order to get more knowledge about it.

The finite state machine to be implemented was really simple, just 2 or 3 states (I don’t remember the precise number now), but still it was very interesting because that was the first time I had to deal with timeouts and gen_fsm. 

Time(out) to dig deeper

As I said in the few lines above, the implementation was fairly easy, so I started coding it and after a while I had a small demo running. The gen_fsm could be started easily and it moved from idle-state to collect-state to send-to_hub-state accordingly to a timeout which was a priori defined using:

-define(TIMEOUT, 5000).

My first demo was coded starting from an idea that came to my mind when I read the project specifications: “Hey, this is gonna be trivial! I saw something about gen_fsm and timeouts on the gen_fsm behaviour page!”. So I started emacs, and wrote:


%% other code here
-define(TIMEOUT, 2000).

-record(state, {temp, hub}).

%% other code here
idle(timeout, State) ->
    %% do some low power stuff here
    {next_state, collect, State, ?TIMEOUT}.

collect(timeout, State) ->
    Temp = get_temperature(),
    {next_state, send, State#state{temp=Temp}, ?TIMEOUT}.

send(timeout, #state{temp=Temp, hub=Hub} = State) ->
    Hub ! {self(), Temp},
    {next_state, idle, State, TIMEOUT}.

%% other code here

I was satisfied with my code and everything seemed right, but after a small session of tests I discovered a bug in it: actually there was a small skew with timeouts, so the finite state machine was not respecting the “every 5 seconds no matter what” rule. But why did I get this bug? After other some good testing I understood that the problem was related to the execution of the code inside each state. This execution took some amount of time, which made my code behave in the wrong way.

To solve this problem I came to the following implementation:

%% other code here

-define(TIMEOUT, 2000).

-record(state, {temp, hub}).

%% other code here

init([]) ->
    State = .....
    timer:apply_interval(?TIMEOUT, ?MODULE, trigger, [self()]),
    {ok, idle, State}.

trigger(Pid) ->
    gen_fsm:send_event(Pid, timeout).

%% other code here

idle(timeout, State) ->
    %% do some low power stuff here
    {next_state, collect, State}.

collect(timeout, State) ->
    Temp = get_temperature(),
    {next_state, send, State#state{temp=Temp}}.

send(timeout, #state{temp=Temp, hub=Hub} = State) ->
    Hub ! {self(), Temp},
    {next_state, idle, State}.

%% other code here

Therefore, using timer:apply_interval/4 in this way I satisfied the “5 seconds no matter what” rule! 🙂 

Should be noticed that I removed all the non salient code (start_link, handle_event and so on) from the code above, so if you don’t know much about gen_fsm you should take a look at one of my previous posts for an insight. 


Categories: English, Erlang Tags: , ,

You should not start your supervisors like this!

August 22, 2011 2 comments

Twice a week I go to StackOverflow and read the new posts about Erlang, doing this I have seen many questions about starting a supervisor using the erl command and the “- s” flag.

This is a topic I really care about, actually when I was coding my first stuff in Erlang, I was asked to code and start from a unix script a small
system composed of a gen_server which was spawned and controlled by a supervisor.

At that time I did’t know Erlang application so I decided to do something like this:

erl -pa ebin -sname mynode -cookie mycookie -s mysupervisor start_link

but this leaded nowhere, the supervisor were not started at all and this was
really frustrating. The thing that annoyed me the most was that by using:

erl -pa ebin -sname mynode -cookie mycookie -s myserver start_link

the gen_server was started successfully, so I thought that the problem was in the supervisor but in fact it wasn’t.

Where was the problem here? Probably, the thing most of people don’t know (and for sure the one I did’t know at the time) is that the supervisor process is linked to the shell process in which it is started. When the calling process (the shell) exits, the supervisor is taken down as well.

To tackle this problem you could take two different approaches:

1) unlink the supervisor from its calling process by using unlink/0
2) package your code using an application

The first solution can be achieved by changing the start_link/0 function of the supervisor as follows:

start_link() -> 
 {ok, Pid} = supervisor:start_link({local, ?SERVER}, ?MODULE, []), 
 unlink(Pid).

Altough this solution solves your problems, I strongly suggest you to adopt the second solution I listed above. Applications in Erlang are really easy to understand and implement. Many information can be gathered online, for example in Mitchell Hashimoto’s blog or in the standard documentation.

Categories: Erlang Tags: , , ,

An introduction to gen_fsm behaviour

September 7, 2010 3 comments

A big truth of gen_fsm is that the online resources are poor: a lot of people keep on writing of gen_server, letting aside this wonderful OTP behaviour. Same thing in most of the Erlang books I have seen so far, usually they just show a flow chart and nothing more, that’s why I decided to write this post.

Unfortunately, when I wrote the code for this post I did not remember very well what kind of example was provided by the official documentation, so it came out that I wrote something very similar to it, 😦 please, forgive me, I will double-check next time! Anyhow maybe you didn’t even know about gen_fsm, so the problem in that case doesn’t exist.

Let’s start!

The gen_fsm is one of the OTP behaviours and it is used to represent a Generic Finite State Machine, it exports a standard set of interface functions and as any other of the OTP behaviours it can be included in a OTP supervision tree or “controlled” by an event manager.

Basically a gen_fsm is nothing more that a piece of code that moves from a “machine state” to another when a specific event happens. You can think of a coffee machine: at the beginning it is in idle, then you put money and press the coffee button, so it goes to a “make coffee” state, then when the coffee is ready it goes again to the idle state.

I know that sometimes the word state is misleading, in fact there are two states in a gen_fsm: a global state in which the finite state machine is, and an internal state that you can imagine as the state of a normal gen_server. Got it?

For my code I wrote the code of a locker, mainly because in my university they built a new library, and now we have a set of super cool lockers…moreover it seemed to me a very good case of finite state machine.

The code is as always a simple example, it just identifies to machine states:  “unlocked”   and  “locked”. Since we students can’t decide our own code, I will put in the internal state the pin code of the locker.

Before the technical part of this article comes, I would like to tell you that if you are using Emacs you can find the skeleton for a gen_fsm under Erlang -> Tools -> Skeletons.

Star_link/0 is the function that  starts a new process representing our gen_fsm and registers it; it is good practice to have a OTP supervisor calling this function directly of indirectly, so that our gen_fsm will be in a supervision tree. This function calls also the method Module:init/1 to initialize itself.

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

In the code above using {local, ?SERVER} we register the gen_fsm with name ?SERVER (a macro to be defined) locally using register/2. A different choice may be to use {global, ?SERVER}, in this case  the gen_fsm is registered globally  using global:register_name/2. If no name is provided, the gen_fsm is not registered.

Note that you can change the function start_link to a function start, but this way you will not be able to fit the new process inside a supervision tree.

Let’s go to the init/1 function now:

init([]) ->
    {ok, unlocked, #state{code=1234}}.

The function init/1 is called by the new process to initialize: if initialization is successful, the function should return {ok, StateName , StateData}, {ok, StateName, StateData, Timeout} or {ok , StateName, StateData, hibernate}, where StateName is the initialglobal state name and StateData the initial internal state of the gen_fsm; in our case we set the state of the machine to “unlocked” and we store in the internal state the code of the locker.

If a timeout is provided (must be an integer) it will be triggered unless an event or a message is received within Timeout milliseconds. The timeout is represented by the atom timeout and should be handled by the Module:StateName/2 callback functions; notice that atom infinity may be used to wait indefinitely, this is the default value.

Hibernate on the other hand is used to make the process  hibernate when waiting for the next message to arrive.

Init/1 may also return {stop,Reason}, if something went wrong.

I told you before that we have two machine states representing our locker, each state can be represented by a function as:

StateName(Event, StateData) ->
    .. code for actions here ...
    {next_state, NewStateName, NewStateData}

where you will change StateName to the state you want to represent and Event to the event you want to handle. StateData is nothing more than the internal state of the gen_fsm. As you can see, you must return a tupla that has as second value the next machine state, and as third value the next internal state.

This is anyhow a definition that will grant us an asynchronous use of the gen_fsm (no reply to the user), if you wish to return a value to the user you should also declare something as:

StateName(Event, From, StateData) ->
    .. code for actions here ...
    {next_state, Reply, NewStateName, NewStateData}

were Reply is obviously the value that goes back to the user.

Here is the code to represent the asynchronous state of the unlocked state:

unlocked({lock, Code}, _From, State) ->
  case State#state.code =:= Code of
    true ->
      {reply, ok, locked, State};
    false ->
      {reply, {error, wrong_code}, unlocked, State}
  end;

unlocked(_Event, _From, State) ->
  Reply = {error, invalid_message},
  {reply, Reply, unlocked, State}.

How can we move from one state to another? The answer is by sending an event: if we are in state S and the event E occurs, we should perform the actions A and make a transition to the state S’. In our case if we are in the unlocked state, and we send to it the event for locking (with the right code), we should move to the locked state.

The function notifying the code lock  event is implemented using gen_fsm:send_event/2 or gen_fsm:sync_send_event/2

The event is made into a message and sent to the gen_fsm.  Let’s see how to implement the event:

lock(Code) ->
  gen_fsm:sync_send_event(?SERVER, {lock, Code}).

As you can see we send to the gen_fsm the event {lock, Code} in a synchronous way; this event will be handled by the current state of the machine. Here is the full code of my gen_fsm, try to compile it and try to send a lock/unlock event in different situations.

-module(locker).

-behaviour(gen_fsm).

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

%% gen_fsm callbacks
-export([init/1, unlocked/2, unlocked/3,  locked/2, locked/3, handle_event/3,
	 handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).

-export([lock/1, unlock/1]).

-define(SERVER, ?MODULE).

-record(state, {code}).

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

%%--------------------------------------------------------------------
%% @doc
%% Creates a gen_fsm process which calls Module:init/1 to
%% initialize. To ensure a synchronized start-up procedure, this
%% function does not return until Module:init/1 has returned.
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
    gen_fsm:start_link({local, ?SERVER}, ?MODULE, [], []).

unlock(Code) ->
  gen_fsm:sync_send_event(?SERVER, {unlock, Code}).

lock(Code) ->
  gen_fsm:sync_send_event(?SERVER, {lock, Code}).

%%%===================================================================
%%% gen_fsm callbacks
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Whenever a gen_fsm is started using gen_fsm:start/[3,4] or
%% gen_fsm:start_link/[3,4], this function is called by the new
%% process to initialize.
%%
%% @spec init(Args) -> {ok, StateName, State} |
%%                     {ok, StateName, State, Timeout} |
%%                     ignore |
%%                     {stop, StopReason}
%% @end
%%--------------------------------------------------------------------
init([]) ->
    {ok, unlocked, #state{code=1234}}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% There should be one instance of this function for each possible
%% state name. Whenever a gen_fsm receives an event sent using
%% gen_fsm:send_event/2, the instance of this function with the same
%% name as the current state name StateName is called to handle
%% the event. It is also called if a timeout occurs.
%%
%% @spec state_name(Event, State) ->
%%                   {next_state, NextStateName, NextState} |
%%                   {next_state, NextStateName, NextState, Timeout} |
%%                   {stop, Reason, NewState}
%% @end
%%--------------------------------------------------------------------
unlocked(_Event, State) ->
  {next_state, unlocked, State}.

locked(_Event, State) ->
  {next_state, locked, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% There should be one instance of this function for each possible
%% state name. Whenever a gen_fsm receives an event sent using
%% gen_fsm:sync_send_event/[2,3], the instance of this function with
%% the same name as the current state name StateName is called to
%% handle the event.
%%
%% @spec state_name(Event, From, State) ->
%%                   {next_state, NextStateName, NextState} |
%%                   {next_state, NextStateName, NextState, Timeout} |
%%                   {reply, Reply, NextStateName, NextState} |
%%                   {reply, Reply, NextStateName, NextState, Timeout} |
%%                   {stop, Reason, NewState} |
%%                   {stop, Reason, Reply, NewState}
%% @end
%%--------------------------------------------------------------------
unlocked({lock, Code}, _From, State) ->
  case State#state.code =:= Code of
    true ->
      {reply, ok, locked, State};
    false ->
      {reply, {error, wrong_code}, unlocked, State}
  end;

unlocked(_Event, _From, State) ->
  Reply = {error, invalid_message},
  {reply, Reply, unlocked, State}.

locked({unlock, Code}, _From, State) ->
  case State#state.code =:= Code of
    true ->
      {reply, ok, unlocked, State};
    false ->
      {reply, {error, wrong_code}, locked, State}
  end;

locked(_Event, _From, State) ->
  Reply = {error, invalid_message},
  {reply, Reply, locked, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Whenever a gen_fsm receives an event sent using
%% gen_fsm:send_all_state_event/2, this function is called to handle
%% the event.
%%
%% @spec handle_event(Event, StateName, State) ->
%%                   {next_state, NextStateName, NextState} |
%%                   {next_state, NextStateName, NextState, Timeout} |
%%                   {stop, Reason, NewState}
%% @end
%%--------------------------------------------------------------------
handle_event(_Event, StateName, State) ->
    {next_state, StateName, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Whenever a gen_fsm receives an event sent using
%% gen_fsm:sync_send_all_state_event/[2,3], this function is called
%% to handle the event.
%%
%% @spec handle_sync_event(Event, From, StateName, State) ->
%%                   {next_state, NextStateName, NextState} |
%%                   {next_state, NextStateName, NextState, Timeout} |
%%                   {reply, Reply, NextStateName, NextState} |
%%                   {reply, Reply, NextStateName, NextState, Timeout} |
%%                   {stop, Reason, NewState} |
%%                   {stop, Reason, Reply, NewState}
%% @end
%%--------------------------------------------------------------------
handle_sync_event(_Event, _From, StateName, State) ->
    Reply = ok,
    {reply, Reply, StateName, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_fsm when it receives any
%% message other than a synchronous or asynchronous event
%% (or a system message).
%%
%% @spec handle_info(Info,StateName,State) ->
%%                   {next_state, NextStateName, NextState} |
%%                   {next_state, NextStateName, NextState, Timeout} |
%%                   {stop, Reason, NewState}
%% @end
%%--------------------------------------------------------------------
handle_info(_Info, StateName, State) ->
    {next_state, StateName, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_fsm 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_fsm terminates with
%% Reason. The return value is ignored.
%%
%% @spec terminate(Reason, StateName, State) -> void()
%% @end
%%--------------------------------------------------------------------
terminate(_Reason, _StateName, _State) ->
    ok.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Convert process state when code is changed
%%
%% @spec code_change(OldVsn, StateName, State, Extra) ->
%%                   {ok, StateName, NewState}
%% @end
%%--------------------------------------------------------------------
code_change(_OldVsn, StateName, State, _Extra) ->
    {ok, StateName, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================
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!

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: , ,