Archive

Posts Tagged ‘gen_fsm’

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

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