#2907 closed Bugs (invalid)
Events no longer reach orthogonal inner state after leaving that state and then returning to it
Reported by: | Owned by: | Andreas Huber | |
---|---|---|---|
Milestone: | To Be Determined | Component: | statechart |
Version: | Boost 1.37.0 | Severity: | Problem |
Keywords: | event fowarding orthogonal inner state | Cc: |
Description
See attached file for an example of the bug.
A state-machine contains a state "parent". "parent" consists of two orthogonal states with initial states "child1A" and "child2A".
There is one event "update" that is handled by both "child1A" and "parent" through custom reactions. "child1A" completes its reaction by returning "forward_event", "parent" does so by returning "discard_event".
When initialized in this manner, the state machine consists of the states "parent" -> "child1A" + "child2A".
Processing an "update" event correctly triggers reactions first in "child1A" and then in "parent".
Processing a "toggle" event causes state "child1A" to transit to "child1B", so the machine now contains the following states:
"parent" -> "child1B" + "child2A"
"child1B" does not handle "update" events and thus, when processing an "update" event, the expected reaction in "parent" is called.
However, if processing a "toggle" event again, which causes state "child1B" to transit back to state "child1A", causes a problem.
I would expect the statemachine to be back in the state:
"parent" -> "child1A" + "child2A"
and debugger inspection tells me it is.
However, when now processing an "update" event, the reaction in "child1A" is NOT called, but should be.
Removing state "child2A" from the state-machine and thus not having any orthogonal regions in "parent" causes the bug to disappear.
Attachments (1)
Change History (4)
by , 14 years ago
Attachment: | boost.ticket.2907.cpp added |
---|
comment:1 by , 14 years ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
The observed behavior stems from a documented limitation of Boost.Statechart:
<http://www.boost.org/doc/libs/1_38_0/libs/statechart/doc/rationale.html#Limitations> (see subsection "Event dispatch to orthogonal regions")
For a precise but possibly hard to read description of the event dispatch algorithm, please see:
<http://www.boost.org/doc/libs/1_38_0/libs/statechart/doc/reference.html#process_event>
In a nutshell, the event dispatch algorithm does the following for each event:
- Starts a new reaction search.
- Selects an *arbitrary* but in this reaction search not yet visited state from all the currently active innermost states. If no such state exists then reaction search is considered unsuccessful.
- If the current state or any of its outer states has a suitable reaction for the event, the event is dispatched. Reactions defined in inner states take precedence over reactions of the outer states. If no suitable reaction has been found then reaction search resumes with step 2.
Note that reactions defined in outer states are checked *before* reactions defined in other innermost states.
To explain things in your example:
- In steps 1-3, before the event is dispatched, state_1A is the first innermost state that is selected. So, for all events this state and its outer state are first checked for a suitale reaction.
- In steps 4-6, before the event is dispatched, state_2A is the first innermost state that is selected. So, for all events this state and its outer state are first checked for a suitale reaction.
In step 2, state_1A does have a reaction for update_event, so it is selected. In step 6, state_2A does not have a reaction for update_event so the reaction of its outer state (parent_state) is selected. Consequently, if you remove the update_event reaction in parent_state then everything should work as expected.
(Note that the documentation says that the order in which the innermost states are selected during reaction search is arbitrary, so you should not write code that depends on the selection order.)
follow-up: 3 comment:2 by , 14 years ago
The documented limitation states "(Boost.Statechart)... stops searching for reactions as soon as it has found one suitable for the current event". I interpret this as follows:
If there are multiple orthogonal states specifying reactions for the same event, then only one of those reactions is triggered. That's at least how it reads to me.
I also deduced that at least one of those reactions will be triggered, but this does not seem to be the case. Am I correct in this conclusion?
Your explanation of the behavior in steps 4-6 is that the event processing mechanism selects one inner orthogonal state (seemingly at random) and if that one does not specify a reaction to the current event, then no other inner orthogonal states are checked and the event is checked against the next outer state.
Is this correct?
comment:3 by , 14 years ago
If there are multiple orthogonal states specifying reactions for the same event, then only one of those reactions is triggered. That's at least how it reads to me.
Correct.
I also deduced that at least one of those reactions will be triggered, [...]
Correct.
[...] but this does not seem to be the case. Am I correct in this conclusion?
Incorrect, in your testcase, as a result of step 6 parent_state::react is called. This is due to the fact that state_2A is the first innermost state that is selected during reaction search. state_2A does not have a reaction for the event, so its outer state (parent_state) is automatically checked for a suitable reaction. parent_state::react simply discards the event, so reaction search is considered successful. If parent_state::react returned forward_event() (or didn't exist) then reaction search would continue with the last remaining innermost state (state_1A).
Your explanation of the behavior in steps 4-6 is that the event processing mechanism selects one inner orthogonal state (seemingly at random) and if that one does not specify a reaction to the current event, then no other inner orthogonal states are checked and the event is checked against the next outer state.
Is this correct?
Not quite: The other innermost states are checked if and only if all the direct and indirect outer states of the selected innermost state are "unable" to react to the event. That is, they all either do not have a reaction for the current event or the reactions all return forward_event().
Test-case working on MSVC 9 SP1 with Boost 1.37.0