[erlang-questions] gen_statem confusion

Vans S <>
Fri Jan 20 16:58:59 CET 2017


> There is no priority between types.  First to enqueue is first to dequeue,
> except for {next_event,_,_} actions that will be _before_ first to dequeue.
This is logical and simple to reason about. Nice.

> So when the bug above has been fixed an action {next_event,state_timeout,_}
> will produce an event that would be dequeued before the event from an action
> {state_timeout,0,_}, with possible already enqueued events in between.

> Furthermore multiple {next_event,state_timeout,_} actions will produce
> one event each, but multiple {state_timeout,0,_} actions will override
> each other so the last one wins hence only produce one event.

Got it. One of my projects is itching to have state_timeout.

Right now I am working with gen_statem to handle netsplits and I think there
maybe a need to have a way for the gen_statem to not process any call/cast/info
until it gets to a certain state.  Take for example we have a need to implement 
pairs trading for stocks. Using states such as:

step0_join_distribution
step1_subscribe_to_ticker1
step2_subscribe_to_ticker2
step3_process_messages

Our tree looks like:

nodedown -> {next_state, step0_join_distribution, {state_timeout, 10000, _}}
step0_join_distribution
  - monitor_nodes_for_nodedown

step1_subscribe_to_ticker1
  - udp:subscribe
  - if subscribed -> monitor_for_sub_loss, step2_subscribe_to_ticker2
  - else {state_timeout, 1000, _}

step2_subscribe_to_ticker2
  - udp:subscribe
  - if subscribed -> monitor_for_sub_loss, step3_process_messages
  - else {state_timeout, 1000, _}
step3_process_messages
  - Now we can process messages normally that come from 
  - other parts of the distribution.


The issue I am grappling with now is if we fail on the subscription to the 3rd party API 
(subscription via udp or tcp) we need to try again.  Trying again we use state_timeout
but this opens a gap for messages to be processed.  A possible approach would be to have a 
catch_all handler for cast/call/info for all states that =/= step3_process_messages, it can
offload the mailbox to state for later processing or reply with a certain error like 
not_ready_for_msgs.

But yea just some thoughts on use cases.



On Friday, January 20, 2017 3:03 AM, Raimo Niskanen <> wrote:
On Thu, Jan 19, 2017 at 04:35:19PM +0000, Vans S wrote:
> Okay same problem with R19.2.1
> 
> 
> 
> On Thursday, January 19, 2017 11:15 AM, Vans S <> wrote:
> Yea ignore that. That is because I was not up to R19.2 when I tested. 
> Let me get up to date.
> 
> 
> 
> 
> On Thursday, January 19, 2017 11:08 AM, Vans S <> wrote:
> Actually I jumped the gun here:
> 
> 
> using a state_timeout as next event which is a valid 
> event_type() as said in the docs produces:
> 
> ** Reason for termination = error:{bad_action_from_state_function,
> {next_event,state_timeout,hi}}
> So state_timeout cant be used here. 
> 
> timeout,  cast, info can be used.
> 
> {call, pid()} also cant be used. 
> 

That is a bug!

I missed to add a clause in event_type/1 for state_timeout when I
introduced it.  The missing clause for {call,From} has always not
been there - amazing nobody noticed!
So when the bug above has been fixed an action {next_event,state_timeout,_}
will produce an event that would be dequeued before the event from an action
{state_timeout,0,_}, with possible already enqueued events in between.

Furthermore multiple {next_event,state_timeout,_} actions will produce
one event each, but multiple {state_timeout,0,_} actions will override
each other so the last one wins hence only produce one event.

I guess generating events of type 'internal' is the really useful
feature - the other types are mostly of theoretical value.
Using them will most probably be confusing.

I will try to squeeze in to fix that for 19.3.

> 
> 
> 
> On Thursday, January 19, 2017 11:01 AM, Vans S <> wrote:
> > So if you in a state callback insert more next_event messages these are
> > inserted first in the queue, and a state_timeout 0 message is inserted last
> > in the queue.
> 
> This clarifies things nicely.  Also what if one was to use {next_event, state_timeout .. vs {state_timeout ..
> Also is there a priority such as external, internal, timeout, state_timeout, etc?

There is no priority between types.  First to enqueue is first to dequeue,
except for {next_event,_,_} actions that will be _before_ first to dequeue.

> 
> Or anything with next_event goes first into the queue (or top of stack if using {next_event, external .. ?) 
> regardless queue or stack the idea is the same, it will be first to dequeue/pop. And this will happen before receive 
> is called.

Precisely.  All {next_event,_,_} actions in an event handler return will
be the first to dequeue for the next event handler, and the order between
them is that the first in the event handler action list will be the first
to dequeue. 

So when the bug above has been fixed an action {next_event,state_timeout,_}
will produce an event that would be dequeued before the event from an action
{state_timeout,0,_}, with possible already enqueued events in between.

Furthermore multiple {next_event,state_timeout,_} actions will produce
one event each, but multiple {state_timeout,0,_} actions will override
each other so the last one wins hence only produce one event.


> 
> 
> 
> 
> 
> On Thursday, January 19, 2017 5:14 AM, Raimo Niskanen <> wrote:
> On Thu, Jan 19, 2017 at 11:59:37AM +0300, Alex S. wrote:
> 
> > 
> > > 19 янв. 2017 г., в 6:01, Vans S <> написал(а):
> > > 
> > > 
> > > 
> > > This wording is really confusing:
> > > 
> > >> instead the the time-out event is enqueued to ensure 
> > >> that it gets processed before any not yet received 
> > >> external event
> > > Because when I tried a test case
> > > 
> > > init() ->
> > >  send(self(), hi),
> > >  {_,_,_,0}.  %0 timeout basically
> > > 
> > > The 0 timeout procced before the 'hi' message.
> > > Using 1 as the timeout, 'hi' message procs first.
> > > 
> > > But it says not yet received external event.  To me send() seems like 
> > > an external event. Hence confusing.
> > Well, it was sent, but not yet received by the gen_statem’s main loop.
> 
> Perfectly correct.
> 
> While gen_statem processes enqueued events it is guaranteed to not enter a
> receive statement, so no new messages are received until after ther
> state_timeout 0 message.
> 
> Other enqueued messages that are being processed such as next_event
> messages will be processed before a state_timeout 0 message.
> 
> So if you in a state callback insert more next_event messages these are
> inserted first in the queue, and a state_timeout 0 message is inserted last
> in the queue.

-- 

/ Raimo Niskanen, Erlang/OTP, Ericsson AB
_______________________________________________
erlang-questions mailing list

http://erlang.org/mailman/listinfo/erlang-question


More information about the erlang-questions mailing list