[erlang-questions] gen_statem and multiple timeout vs 1

Raimo Niskanen <>
Fri Oct 7 17:10:11 CEST 2016


On Fri, Oct 07, 2016 at 02:38:58PM +0000, Vans S wrote:
> > Your mail client really messed the code up so I had to reformat it
> > before reading it...
> 
> 
> Il make sure to send email as plain text.

:-)

> 
> 
> > handle_event(internal, {timeout,TimerRef,keep_alive, S, #{named_timeout := #{keep_alive := TimerRef}} = D) ->
> 
> I looked at timer functions and something interesting is I don't see any way to retrieve the remaining time of a TimerRef,
> nor any way to bump it.  Maybe I missed something?

That is right.  With the gen_* model it is not possible to reach the
engine state from the callback code other than by returning actions,
and they can not return values back to the callback code.

But you can bump a timer by restarting it, which is normally what you want
when you talk about bumping a timer.  That is: start the timer with
the time I say from now and cancel the running timer.

> 
> 
> Another possible option is {named_timeout, incr|decr|set, 15000, {built, BuildUUID}}, so the 3 size tuple implies set.

Yes. But how useful is incrementing and decrementing a running timer?

> 
> This does not solve referencing the time thought.  

Nope.  You need to hold the TimerRef in the callback state for this.
Use erlang:start_timer/3,4.

> 
> If named_timeout/n returns a TimerRef, we save that in our state, and there is a way to incr/decr/set/time_remaining it. 
> I think then this is ideal.

An action can not return a value, as I explained above.  If you want a
TimerRef to save in the callback state - use erlang:start_timer/3,4.  Read
the manual of erlang:cancel_timer/2,3: it returns the time left so you can
then calculate a new timer time to set.  This API also allows you to set
timeouts on absolute times instead of relative...  There is little reason
to implement wrappers for this.

> 
> Maybe also expose named_timeout/2 which will be simpler and return {named_timeout, 15000, Name}. 
> For those who do not want to track TimerRef.

A function called from the callback module can not affect the engine state.
It has to be done from an action that is processed by the engine after the
callback module returns from the state function.

You seem to desire a timer helper library...

> 
> 
> A possible extra consideration is if we want to pass extra non-key data for the timer callback. Right now we can only pass 
> Name, and we consider Name the timer key.  What if we want something like this {named_timeout, 15000, {built, BuildUUID, BuildingUniqueState}}.

Using erlang:start_timer/3,4 you can accomplish this.

> 
> This way we MAY gain some extra flexibility.  I can't think of a practical use case now, but there may be.  Now we cannot cancel this timer. As 
> BuildingUniqueState is not referenced anywhere except inside the callback.  Making a 4 size tuple can remedy this.  I am not sure 
> where this fits thought.  Anyways this is another consideration that might have a use.
> 
> 
> {named_timeout, 15000, {built, BuildUUID}, BuildingUniqueState}

That is possible.  {named_timeout, Time, Name, Msg} with the call signature
    StateName({named_timeout,Name}, Msg, Data)

But I think often it is just annoying with the Msg term in {state_timeout,T,Msg}
and {timeout,T,Msg}.  So not having it in {named_timeout,T,Name} is either
a blessing or an anomaly...  Or it is with named_timer the Msg may actually
be useful. :-)


-- 

/ Raimo Niskanen, Erlang/OTP, Ericsson AB


More information about the erlang-questions mailing list