[erlang-questions] gen_statem and multiple timeout vs 1
Fred Hebert
mononcqc@REDACTED
Wed Oct 5 05:52:38 CEST 2016
On 10/04, Vans S wrote:
>moving, attacking, jumping, stopped, moving_attacking,
>moving_attacking_jumping, attacking_jumping, moving_jumping,
>jumping_stopped, .. etc
>
>From what your saying, the state machine needs to be in ONLY ONE of these states at a time.
>
Well not necessarily, gen_statem now lets you use complex terms as
state. So the state could be [moving, attacking, jumping], though not
quite sure how you'd deal with these overlapped states. But it's
possible to use a more abstract data structure than an atom to represent
states now.
I.e. a timeout for running being triggered could transition you into a
thing like:
handle_event({timeout, Ref, running}, ComplexState, Data) ->
...
NextState = ComplexState -- [running],
...
or whatever format you'd like. This is new stuff and capacities of
gen_statem that was not possible in gen_fsm, so best practices haven't
been fully codified for this yet, but this should certainly be possible.
>
>This is true. But then there needs to be a way to get the timer ref.
> Sometimes Erlangs approach is, just make it work.
>I don't see any way to get the timer ref without including it in the callback OR creating your own timer and including it for tracking (vs just specifying the timeout).
>
Yeah, I'm advocating for creating your own timer, where you do get a ref
for it.
>I did not consider this. This is truly problematic BUT would not the
>current way timeout works run into this same problem? So this should
>not affect allowing multiple timeouts vs a single timeout.
Right now what you can do if you manage your own timers is to cancel the
old ones or ignore them (you've got their refs so it's easy) and you can
set new ones right away. You can't do that however if you rely on the
return tuple of a state transition to do it, since that transition
cannot happen from a code_change callback.
>Learning from that, I rather this be inside gen_statem. If its not, I
>would have no problem to write my own little timer library for
>cancel_timer+purging mailbox.
>
Usually the pattern I use is to write a short 'reset_<whatever>_timer'
function that returns its ref, and then I can track the ref in my state.
I always found this fairly convenient to deal with things, and for
example, I mostly don't clean up timers very hard. I.e. you can make use
of the newer cancel_timer options for async returns with or without
info, and by matching on specific refs, I can just ignore timers that
are for events I know are no longer up for consideration and let them
disappear, and automatically can ignore a bunch of potential race
conditions (i.e. no mailbox cleanup, just ignore messages coming in)
This is also neat to avoid whatever blocking or synchronization that
could take place on timer management, but in some cases you may still
want to do that.
>Since you are only in ONE state at a time, you automatically know if
>the timeout happened, it MUST correspond to the current state we are
>in.
>
Does it? Couldn't the timeout event have been postponed, and therefore
come from a prior state change? I believe so. There's no relationship
between an event being handled in a state and that event having been
sent in that state.
>Another option is to remove the timeout then. It just seems out of place to me.
>
At the two extremes are the positions that all timeout management is
manual, and that gen_statem is to replace full-on control of timeouts
people can do manually (with all the cancellation options and whatnot).
Those are the two extremes, and the current thing is where on the
gradient does the timeout implementation currently lie. So far it
appears to be a fairly minimal convenience factor. I'm just wondering if
it's worth pushing it further on the gradient of "emulating all the
flexibility of manual control."
>What is the use case of a single timeout with a UNIQUE EventContent?
>
The current semantics are specific about one thing:
Generates an event of event_type() timeout after this time (in
milliseconds) unless another event arrives in which case this
time-out is cancelled. Notice that a retried or inserted event
counts like a new in this respect.
These timeouts are mostly used for one thing in my experience: tracking
inactivity of the state machine. gen_servers and gen_fsms have the same
one. The only pragmatic use case I've made of them were to give myself a
delay of N milliseconds before which it would be reasonable to send the
process in hibernation, GCing and compacting its memory at once, since
it had not received a message in that time otherwise.
All other timeouts I tend to manage by hand because I do not want them
to be interruptible by the arrival of other messages in the mailbox of
the process.
This is a very, very important detail! Those semantics are also a lot
trickier to handle by yourself (you'd need to put them in every clause)
than most other timers you could imagine using. This makes them a fairly
good idea to include in the OTP behaviours themselves, as opposed to
most other timer usages, IMO.
Regards,
Fred.
More information about the erlang-questions
mailing list