Boost logo

Boost :

Subject: Re: [boost] [msm] guard behavior if guard guards the transition MSM threats event as handled is that correct ?
From: Richard Szabo (sz.richard_at_[hidden])
Date: 2011-11-24 04:32:20


Hi Christophe

I'm using boost trunk from yesterday And I have modified my example to
use your suggestion :
Row < SubRunning , event3    , none       , Defer , none /* or guard */ >

will do.

Well, there is a small bug, but not where you expect it. It seems that
the processing continues even though this transition defers the event.
I just fixed this in the trunk (rev. 75641).

Here is the execution output of the example attached :

MS1 Entering State: Idle by: event1
MS1 Leaving State: Idle by: event1
MS1 Entering State: SubRunning by: event1
FwdGuard: returns:1, event: event2
Processing BaseEvent Instance(event2)
Setting guard false
FwdGuard: returns:0, event: event3
MS1 DEFERED: event3
FwdGuard: returns:0, event: event4
FwdGuard: returns:0, event: event3
MS1 DEFERED: event3
FwdGuard: returns:0, event: event5
MS1 Leaving State: SubRunning by: event5
MS1 Entering State: AfterSub by: event5
MS1 no_transition event (event3)

It seems that the bug not fixed ... but this is my least worries (
partly my fault You have asked me to test it for you .... but I did
not sorry about that).

My bigger problem is the interpretation of  no_transition you saying
that it is an error situation... Well I have in my code a lot of
situation when there are signals which are not interested to my state
machine but they are dispatched to me anyhow our modules are used by
different places for different purposes and there are signals which
are useful in one case but not needed for my purpose I just simply
ignore them these are ending up in no_transition so there I just write
a log entry that signal is not handled so for me it is not an error at
all it is the intended behavior.

Now back to the My blocking problem .....

We use at the moment about 36 MSM state machines out of this 26 are
performing specific well defined configuration task these 26
state-machine are nested to each in to each other to perform
complicated behaviors sometimes there are nesting goes done to 5 level
deep. We have,  I call them main state machines which are connect
these small task to each other to build complex behavior. We have more
than 500 different row entries spitted in to these state machines.
At the beginning  we try to use MSM way of state machine nesting and
soon we figured out that there is no compiler in the word which can
compile/link this code.
Than I have decided to make smaller compile unit and make each state
machine as a separate compile unit and let communicate them over a
simple interface.
Yes this slows done the dispatching because we have to re-dispatch
each event in run-time as many times as many nesting level we have.
But this tread of is acceptable. And yes this way state machines are
holding references to outer state machines  if it is running as a
nested state machine and states which are nested state machines are
holding references to inner state machine instances. Every state
machine uses the same interface to communicate and we hide the
construction of the state machines with factories. So this way the
outer does not exposed to the inner the inner does not exposed to the
outer leads us to a compile-able code :).
In case the state machine is in a stare where the sate is actually a
holder of an inner state machine there is a Row with a baseEvent which
this case re-dispatches the event to the inner one. Now if the inner
one does not handles the event it shall send it back to the outer one.
My assumption was that I can use the no_transition for this purpose
but the implementation turns out different :(. For me name
no_transition suggests that if no transition triggered. This  function
will be called and if the guard rejects the transition than this means
to me that the transition is not triggered.
Anyhow we could argue on it but in the end it does not help with my problem ....

I would like to ask you to change the implementation .... if it is
possible .....  :)

I think my way would help in case of debugging because if the state
machine does not triggered a transition at least the user has a chance
to log it or do something with it ....
I find it very annoying if an event is "eaten by a state machine
without knowing what happened"
If you disagree than please help me to find a way to get around of my
problem ....

I see 3 way out of it :

1. change the implementation of no_transition triggering.
2. provide a new function something like default_transition which is
called if the transaction table( including rejected transitions by
guards ) or states (deferral) are not handling the event.
3. A special row something like Row < AllState, AllEvent, None,
MYDefaultTrans, None> eg give a last default trans option to the user
to handle the event.

Or if you have any better idea how to compile my code ?.

Thanks for your help ....
Cheers
Richard

On 23 November 2011 21:49, Christophe Henry
<christophe.j.henry_at_[hidden]> wrote:
>
> Hi Richard,
>
>> the attached example I think shows 2 problems.
>>
>> 1st is that the deferral of event3 is not happening.
>> 2nd is that the no_transition function is never called.
>>
>> the output of the run is :
>>
>> MS1 Entering State: Idle by: event1
>> MS1 Leaving State: Idle by: event1
>> MS1 Entering State: SubRunning by: event1
>> FwdGuard: returns:1, event: event2
>> Processing BaseEvent Instance(event2)
>> Setting guard false
>> FwdGuard: returns:0, event: event3
>> FwdGuard: returns:0, event: event4
>> FwdGuard: returns:0, event: event5
>> MS1 Leaving State: SubRunning by: event5
>> MS1 Entering State: AfterSub by: event5
>>
>> I think it shall be :
>>
>> MS1 Entering State: Idle by: event1
>> MS1 Leaving State: Idle by: event1
>> MS1 Entering State: SubRunning by: event1
>> FwdGuard: returns:1, event: event2
>> Processing BaseEvent Instance(event2)
>> Setting guard false
>> FwdGuard: returns:0, event: event3
>> FwdGuard: returns:0, event: event4
>> MS1 no_transition event (event4)
>> MS1 Leaving State: SubRunning by: event5
>> MS1 Entering State: AfterSub by: event5
>> MS1 no_transition event (event3)
>>
>>
>> So in this example event3 and event4 are never triggered a transition ...
>> the Row < SubRunning , baseEvent , none       , ProcessBaseEvent , FwdGuard
>>>
>>> was guarded by the  FwdGuard for these events.
>>
>> Is my expected behavior wrong ?
>
> Well, hard to answer because the Standard does not discuss conflicts between a state declaration and a transition, so your guess is as good as mine ;-)

>
> Let's say that this is the intended behavior. In the current implementation, the state declaration has the lowest priority. This means the transition table comes first, and if it handles the event, then processing stops here so your deferred declaration has no effect.
>
> The answer to the question in your title is yes. If a guard rejects the event, the event is really handled (rejection is a perfectly acceptable event handling) so there is no call to no_transition, which is to be seen as a catch-all error handler in case you forget to handle an event. Seeing that the default version is an assert, imagine what you'd get at every guard rejection ;-)
>
>> If guard is executed but the transition actually is triggered because it
>> is guarded out why this  event treated as handled event ?.
>
> Because a guard is not an error, a call to no_transition probably is.
> I think the best way to solve the conflict between state declaration and the transition table wanting to process is to state what you want in the transition table itself:
>
> Row < SubRunning , event3    , none       , Defer , none /* or guard */ >
>
> will do.
>
> Well, there is a small bug, but not where you expect it. It seems that the processing continues even though this transition defers the event. I just fixed this in the trunk (rev. 75641).
>
>> This 2 problems breaks my current nested state machine implementation
>> because I'm using  no_transition to forward the event to the outer state
>> machine. I'm confused :(.
>
> I think this will not work (and should not) because there is no call to no_transition in a submachine (I don't see it here but I suppose your real code is bigger). The reason is the same as above, no_transition is an error and will assert. But if a submachine cannot process an event, it is not necessarily an error, the outer machine should get its chance to process the event. I'm also afraid you're trying to keep a pointer to the outer machine to process the event (cycle). I suggest pushing the event to a pseudo exit, which will achieve the same a better way.
>
> HTH,
> Christophe
>
>
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost




Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk