[erlang-questions] gen_statem and multiple timeout vs 1

Raimo Niskanen raimo+erlang-questions@REDACTED
Mon Sep 26 16:56:25 CEST 2016


On Sun, Sep 25, 2016 at 05:32:19PM +0000, Vans S wrote:
> Learning the new gen_statem made me desire for one extra feature.
> 
> Say you have a common use case of a webserver /w websockets, you have a general connection timeout of 120s, if no messages are received in 120s it means the socket is in an unknown state, and should be closed.
> 
> So you write your returns like this anytime the client sends you a message:
> 
> {next_state, NextState, NewData, {timeout, 120*1000, idle_timeout}}
> 
> Now if the client does not send any messages in 120 seconds, we will get a idle_timeout message sent to the gen_statem process.
> 
> Awesome.
> 
> But enter a little complexity, enter websockets.
> 
> Now we need to send a ping from the gen_statem every 15s to the client, but we also need to consider if we did not get any messages from the client in 120s, we are in unknown state and should terminate the connection.
> 
> So now we are just itching to do this on init:
> 
> {ok, initial_state, Data, [        {timeout, 120*1000, idle_timeout},        {timeout, 15*1000, websocket_ping}
>     ]}
> 
> This way we do not need to manage our own timers using erlang:send_after.  timer module is not even a consideration due to how inefficient it is at scaling.
> 
> But of course we cannot do this, the latest timeout will always override any previous.
> 
> What do you think?

Your use case is in the middle ground between the existing event timeout
and using erlang:start_timer/4,3, and is a special case of using
erlang:start_timer/4,3.

The existing {timeout,T,Msg} is an *event* timeout, so you get either an
event or the timeout.  The timer is cancelled by the first event.
This semantics is easy to reason about and has got a fairly simple
implementation in the state machine engine partly since it only needs
to store one timer ref.

It seems you could use a state timeout, i.e the timeout is cancelled when
the state changes.  This would require the state machine engine to hold any
number of timer refs and cancel all during a state change.

This semantics is subtly similar to the current event timeout.  It would
need a new option, e.g {state_timeout,T,Msg}.

The {state_timeout,_,_} semantics would be just a special case of using
erlang:start_timer/4,3, keep your timer ref in the server state and cancel
it when needed, since in the general case you might want to cancel the
timer at some other state change or maybe not a state change but an event.

So the question is if a {state_timeout,_,_} feature that auto cancels the
timer at the first state change is so useful that it is worthy of being
implemented?  It is not _that_ much code that is needed to store
a timer ref and cancel the timer started with erlang:start_timer/4,3,
and it is more flexible.

I implemented the {timeout,_,_} feature just so make it easy to port from
gen_fsm.  Otherwise I thought that using explicit timers was easy enough.

-- 

/ Raimo Niskanen, Erlang/OTP, Ericsson AB



More information about the erlang-questions mailing list