Boost logo

Boost Users :

Subject: Re: [Boost-users] [StateChart] event processing order in async machine
From: Igor R (boost.lists_at_[hidden])
Date: 2010-10-20 16:21:50


Hi Andreas,

Thanks for your response!

> As far as I can tell, the phenomenon you're observing would maifest itself in
> the same way in synchronous machines as it does in your async machine. What
> follows therefore only considers the properties that are common to both types
> of machines.

Yes, of course, async machine is stuck in the topic name and in my
repro just for "historical reasons" :).

> 1. Each state machine has a general event queue.
> 2. Logically, there is a *per-state* deferred event queue, which stores
> incoming events that are deferred while the machine resides in a given state.
> Whenever a state is left, all deferred events for that state are moved into
> the general event queue.
> 3. As the last step of state_machine::process_event, all events in the general
> event queue are processed.
>
> IIUC, what you're observing can be explained with the 3 points above, right?

Right. To be more exact, it's because in (2) "all deferred events for
that state" are *enqueued* into the general event queue, i.e. pushed
in fifo manner. Although, intuitively I'd say that these deferred
events have "higher priority" than those in the queue, because they
were posted earlier for sure. So this is exactly the question: wasn't
it more appropriate to push them to front of the "queue" (well, not
real queue then)?

> In your example, When s1 is left, instances of ev3to4_1 and ev3to4_2 are moved
> from the deferral queue to the general event queue, although the next state
> does nothing else but defer them also. IMO, the root of the problem lies here.
> A better solution would be to put both s1 and s2 into an outer state (e.g. s)
> and have that defer ev3to4_1 and ev3to4_2. s1 would then only defer ev2to3 and
> transition to s2. Similarly, s2 would then only transition to s3.

It's a nice workaround for this specific case (which is merely a
minimal reproduction), but if the main idea behind it is to avoid
multiple deferrals of the same event, then I'm afraid it would be
quite complicated to apply it to my real FSM, because it has rather
long "pipeline" of states, some of them are already nested.
It looks like this (indentations mean substates):

struct Disconnected;
struct Connecting;
struct Connected;
--struct Unauthenticated;
--struct Authenticating;
--struct Authenticated;
----struct Stopped;
----struct Starting;
----struct Started;
...and so on...

The main events (are queued in this order):
struct EvConnect;
struct EvAuthenticate;
struct EvStart;
struct EvGetData;

EvConnect moves Disconnected-->Connecting, EvAuthenticate moves
Unauthenticated-->Authenticating, and so on.
These transitions generate some actions, and later "acknowledging" events come:
EvConnect::Ok advances Connecting-->Connected, EvConnect::Fail rolls
back Connecting-->Disconnected, and so on.

So if everything goes well, EvGetData should be deferred until Started
state, where it's processed.
OTO, if some failure occurs, EvGetData would be processed as "error"
in one of "stable" states (Disconnected, Unauthenticated, etc) - it
cannot be silently ignored for some reasons.
This means that EvGetData should never appear in the event queue
before the events, which must precede it, because then it would be
processed immediately as "error".
Unfortunately, this is exactly what happens now: an "acknowledging"
event enters between EvStart and EvGetData (which is legitimate),
causes transition Authenticating-->Authenticated, and the 2 events
come reversed to Stopped state.

I'm really sorry to bother you with my FSM details, but maybe you can
see some obvious workaround that I miss:).

Thank you!


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net