Home > English, Erlang > My brief soliloquy on gen_fsm timeouts

My brief soliloquy on gen_fsm timeouts


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. 


About these ads
Categories: English, Erlang Tags: , ,
  1. dieswaytoofast
    June 25, 2012 at 1:02 pm

    Minor point – the timer module is implemented using the same timeout feature as above, except in a gen_server context – possibly resulting in slightly similar skew effects in the long run. In fact, the docs explicitly tell you that “The timeouts are not exact, but should be at least as long as requested”, with emphasis on “at least”.
    If you really, *really* care about performance, you might probably want to use erlang:send_after/3 erlang:start_timer/3.
    Of course, if you don’t feel like rolling your own, you could always use my timer2 module, which wraps them in a ‘timer API compatible’ wrapper –> https://github.com/dieswaytoofast/timer2

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: