Boost C++ Libraries: Ticket #2907: Events no longer reach orthogonal inner state after leaving that state and then returning to it https://svn.boost.org/trac10/ticket/2907 <p> See attached file for an example of the bug. </p> <p> A state-machine contains a state "parent". "parent" consists of two orthogonal states with initial states "child1A" and "child2A". </p> <p> 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". </p> <p> When initialized in this manner, the state machine consists of the states "parent" -&gt; "child1A" + "child2A". </p> <p> Processing an "update" event correctly triggers reactions first in "child1A" and then in "parent". </p> <p> Processing a "toggle" event causes state "child1A" to transit to "child1B", so the machine now contains the following states: </p> <p> "parent" -&gt; "child1B" + "child2A" </p> <p> "child1B" does not handle "update" events and thus, when processing an "update" event, the expected reaction in "parent" is called. </p> <p> However, if processing a "toggle" event again, which causes state "child1B" to transit back to state "child1A", causes a problem. </p> <p> I would expect the statemachine to be back in the state: </p> <p> "parent" -&gt; "child1A" + "child2A" </p> <p> and debugger inspection tells me it is. </p> <p> However, when now processing an "update" event, the reaction in "child1A" is NOT called, but should be. </p> <p> Removing state "child2A" from the state-machine and thus not having any orthogonal regions in "parent" causes the bug to disappear. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/2907 Trac 1.4.3 George van Venrooij <george.van.venrooij@…> Fri, 03 Apr 2009 10:11:30 GMT attachment set https://svn.boost.org/trac10/ticket/2907 https://svn.boost.org/trac10/ticket/2907 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">boost.ticket.2907.cpp</span> </li> </ul> <p> Test-case working on MSVC 9 SP1 with Boost 1.37.0 </p> Ticket Andreas Huber Fri, 03 Apr 2009 16:01:54 GMT status changed; resolution set https://svn.boost.org/trac10/ticket/2907#comment:1 https://svn.boost.org/trac10/ticket/2907#comment:1 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">invalid</span> </li> </ul> <p> The observed behavior stems from a documented limitation of Boost.Statechart: </p> <p> &lt;<a href="http://www.boost.org/doc/libs/1_38_0/libs/statechart/doc/rationale.html#Limitations">http://www.boost.org/doc/libs/1_38_0/libs/statechart/doc/rationale.html#Limitations</a>&gt; (see subsection "Event dispatch to orthogonal regions") </p> <p> For a precise but possibly hard to read description of the event dispatch algorithm, please see: </p> <p> &lt;<a href="http://www.boost.org/doc/libs/1_38_0/libs/statechart/doc/reference.html#process_event">http://www.boost.org/doc/libs/1_38_0/libs/statechart/doc/reference.html#process_event</a>&gt; </p> <p> In a nutshell, the event dispatch algorithm does the following for each event: </p> <ol><li>Starts a new reaction search. </li></ol><ol start="2"><li>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. </li></ol><ol start="3"><li>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. </li></ol><p> Note that reactions defined in outer states are checked *before* reactions defined in other innermost states. </p> <p> To explain things in your example: </p> <ul><li>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. </li></ul><ul><li>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. </li></ul><p> 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. </p> <p> (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.) </p> Ticket George van Venrooij <george.van.venrooij@…> Tue, 07 Apr 2009 13:37:17 GMT <link>https://svn.boost.org/trac10/ticket/2907#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/2907#comment:2</guid> <description> <p> 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: </p> <p> If there are <strong>multiple</strong> orthogonal states specifying reactions for the <strong>same</strong> event, then <em>only one</em> of those reactions is triggered. That's at least how it reads to me. </p> <p> I also deduced that <em>at least one</em> of those reactions will be triggered, but this does not seem to be the case. Am I correct in this conclusion? </p> <p> 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. </p> <p> Is this correct? </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Andreas Huber</dc:creator> <pubDate>Tue, 07 Apr 2009 18:19:08 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/2907#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/2907#comment:3</guid> <description> <blockquote class="citation"> <p> If there are <strong>multiple</strong> orthogonal states specifying reactions for the <strong>same</strong> event, then <em>only one</em> of those reactions is triggered. That's at least how it reads to me. </p> </blockquote> <p> Correct. </p> <blockquote class="citation"> <p> I also deduced that <em>at least one</em> of those reactions will be triggered, [...] </p> </blockquote> <p> Correct. </p> <blockquote class="citation"> <p> [...] but this does not seem to be the case. Am I correct in this conclusion? </p> </blockquote> <p> 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). </p> <blockquote class="citation"> <p> 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. </p> <p> Is this correct? </p> </blockquote> <p> Not quite: The other innermost states are checked <em>if and only if</em> 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(). </p> </description> <category>Ticket</category> </item> </channel> </rss>