Boost logo

Boost Users :

Subject: Re: [Boost-users] [MSM] a timer/timeout for the whole state machine (including submachines)
From: Christophe Henry (christophe.j.henry_at_[hidden])
Date: 2012-04-16 17:07:48


>> So far so good, but you are stuck with how to inform client state
>> machines of your timer that there is a timeout, right?
>
> mmm... not exactly...
>
> In fact, the timeout is working fine.
> It is sent from the timer state/region and processed fine in the
> "main" machine/region, and in their submachines.
>
> Is the reset event (only when generated in a submachine of the main
> machine/region) which is really failing.
>
> The reset is always generated when there is "activity" in the main
> machine/region, where I (could) have submachines.
> If there is "activity" in a submachine, the reset event is generated
> in the submachine (but not up-fwd) and then is not processed by the
> timer region of the upper state machine...
>
> So, until now, the *reset in a submachine* is my real problem, not the
> timeout (which work great!).

I see. In this case, it's not very beautiful but you'll have to forward the
reset event from one region to the other. You might need to use a pseudo
exit, then re-enter your submachine. If you use a History, you'll get back
to the substate you were in.

>>> - More work on the pseudo exit?
>> - use the UML way. The timer is a state machine, with a pseudo exit when
>> a timeout is detected. This is an encapsulated submodule used by any fsm.
>> Like a well-behaved submodule, it advertises that timeout events are
>> going to be fired through the pseudo exit. How it is done inside is not
>> the outer fsm's business.
>
> Ok, iiuc, this is a better way to define that a timeout is going to be
> sent outside the state machine.
> And, although now the timeout is not a problem for me, i understand
> that pseudo-exit is the standard way to say "I'm a (full) state
> machine but I'm sending events out of me". is this correct? (i should
> read something about uml right now! ;-)

Yes and "I'm going to exit in the process".

> Then I should probably use them (the pseudo exists) also for the
> resets generated in my submachines...
> I have to read more on pseudo exits...

There is not much actually. It's a bit like a return statement in a
function.

> But here I'm still missing my real problem: how to (say in the
> documentation and to) implement the fact that the timer can/should
> receive "reset" events from any (sub) state in the orthogonal
> region(s).

I'm afraid there is no nice way to document this. You'll have to use
standard code documentation tools.

> May be pseudo-entries...?

I'm not a fan of them. They bring not much documentation help and mix bad
with orthogonal regions (they can enter only one region).

>>> For this reason I could think that a (sub) state machine could handle
>>> some "no_transitions" for some events (some cached cases), but it can
>>> also auto-upper-fwd them to the parent state machine...
>> I understand but it's not the way the Standard is thought (well, at least
>> I think). You're supposed to process an event on the outer, which then
>> uses priority rules to know what's done (deeper first, completion events,
>> deferred events, conflicts, etc.).
>
> I see, but iiuc the standard way is fine for me!
>
> I mean, iiuc, the key difference here between the standard and the msm
> is that in the standard all events are always processed by the outest
> sm (and then it uses the prioty rules), and in msm we can process
> events in the "submachine scope" (in fact, until now, if we hate ugly
> pointers, then we are forced to process events only in that submachine
> scope, right? ;-).

Nope. The Standard actually says very little about queues (except that the
order of dequeuing is up to the author) and I could not find a word about
how a state machine or submachine can process an event to itself. This one
can only be found in the litterature, and I saw it only in the context of a
simple non-hierarchical state machine. How a submachine has to process an
event it sends to itself is nowhere described, which means left to the
author ;-)
But yes to the second point, at the moment msm allows you to process an
event at any level, but for top-level, you will need the ugly pointer.

> Then, iiuc, if msm removes the possibility of processing events
> directly into submachines but the event processing is always done
> starting from the outer state machine, then everything should work
> (and be more standard-conform)?
> Am I missing something here?

It is already standard-conform as not forbidden :)
But if I change this, then it might become non standard-conform (as
previously said, if the submachine has a subsubmachine and I process up, I
violate the priority rules).

> I mean, the upper-fwd is probably a kind of bad idea needed (or
> useful) only because msm allows (now it "forces") event processing
> directly in submachine scopes, if not, every thing should just work?
>
> If I'm right, then, I'm wondering if the processing of events directly
> in submachines is useful in some cases...?
> I mean, I see that access to the submachine can be useful if someone
> has some kind of "global" data members in it (shared by all the
> states) and want to access them...
>
> But the fact of event processing, is really useful in any case?
> Forcing the event processing to start always to the outest state
> machine should not remove any possibility we have now... or it will?

It will be slower as you will need to process from fsm to sub-fsm to
sub-fsm, etc.
Say I'm writing a parser, I'll be unhappy to have msm force me to process
all from the top, handle conflicts, etc.
Then it'll be confusing, because my submachine will handle events very
differently according to the configuration (used as stand-alone, embedded,
with or without submachines). Worse, If I templatize a submachine with a
subsubstate type (which could be a submachine), it will behave quite
differently.
Third, chances are good that I will violate a priority rule.
And fourth, I will break existing code.

> I'm far to be sure, but now I'm thinking something like:
>
> sm::process_event(evt)
> {
> if(this->is_contained()) outer->process_event(evt); // assuming we
> have a pointer to the outer sm...
> else process_event_with_rules(evt);
> }
>
> ok, that's upper-fwd...
> ;-)

And if outer has a outer itself, what happens?
I think there is no general solution. MSM makes the choice that the "fsm"
parameter to actions is the deepest level for performance and style reasons.
I understand that there are cases where it's not what you wish. For this
cases, I favor instead in actions some variadic template where you get all
the fsms on the way, from deepest to outer. This way you can choose what is
the best depth for you to process an event. But it's not implemented yet, so
in the meantime, well, there is an ugly pointer ;-)
(plus knowing the outer pointer type will be pretty hard with C++ but it's
an implementation detail)

Cheers,
Christophe


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