Here is one possible solution. The current problem arises because when we end the 'while running and not stable' loop, running may be false, yet we continue on doing invokes, etc., and then waiting for an external event, with the result that we don't checking running until we have gotten an external event and returned to the top of the loop. It looks to me like the current algorithm will have a problem whenever we enter a final state on an internal event with the external event queue empty.
One fix would be to explicitly check for 'running' at the end of the 'while running and not stable' loop. If we do that, we don't have to check running at the top of the loop. Hence:
procedure mainEventLoop():
new=> while true:
enabledTransitions = null
stable = false
# Here we handle eventless transitions and transitions
# triggered by internal events until machine is stable
while running and not stable:
enabledTransitions = selectEventlessTransitions()
if enabledTransitions.isEmpty():
if internalQueue.isEmpty():
stable = true
else:
internalEvent = internalQueue.dequeue()
datamodel["_event"] = internalEvent
enabledTransitions = selectTransitions(internalEvent)
if not enabledTransitions.isEmpty():
microstep(enabledTransitions.toList())
#check to see if we are in a top-level final state
new=> if not running:
new=> break
# Here we invoke whatever needs to be invoked. The implementation of 'invoke' is platform-specific
for state in statesToInvoke:
for inv in state.invoke:
invoke(inv)
etc...(rest of the loop not modified)
-----Original Message-----
From: Jim Barnett [mailto:Jim.Barnett@genesyslab.com]
Sent: Tuesday, January 29, 2013 10:01 AM
To: Voice Browser Working Group; www-voice@w3.org
Subject: RE: ISSUE-827: bug in detecting final states [SCXML-LC-Comments]
Here's a further detail to consider: a state machine may enter a <final> child of <scxml> on any transition, even if there are still events in the internal event queue. In that case, should the state machine process those events, or should it stop immediately? In some sense, this is a distinction without a difference, because if the state machine is in a <final> child of <scxml>, no transitions are available to take. An external observer will never see a difference between an implementation that processes the remaining events, and one that doesn't. But at the algorithm/code level, the difference is significant.
-----Original Message-----
From: Voice Browser Working Group Issue Tracker [mailto:sysbot+tracker@w3.org]
Sent: Tuesday, January 29, 2013 9:11 AM
To: w3c-voice-wg@w3.org; www-voice@w3.org
Subject: ISSUE-827: bug in detecting final states [SCXML-LC-Comments]
ISSUE-827: bug in detecting final states [SCXML-LC-Comments]
https://www.w3.org/Voice/Group/track/issues/827
Raised by: James Barnett
On product: SCXML-LC-Comments
>From Gavin Kistner:
Consider this test case:
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="HistoryTest">
<state id="s" initial="s1">
<state id="s1">
<onentry><raise event="go"/></onentry>
<transition event="go" target="pass"/>
</state>
<final id="pass"/>
</state>
</scxml>
And this test runner:
final2 = RXSCy.Machine(@data['final2']).start
assert(final2.is_active?('pass'),"final2 should pass")
refute(final2.running?,"final2 should not be running")
In my interpreter, the first assertion works, but the second fails. The state machine enters the 'pass' final state, but the `running` flag is never set to false.
Looking at the algorithm, I don't see where it this case is covered. There are only two spots where running is set to false. The first is when receiving an external cancel event during the mainEventLoop, and the other is at the end of enterStates:
for s in configuration:
if isFinalState(s) and isScxmlState(s.parent):
running = false
1) Is my test case wrong? Should entering the 'pass' final above not kill the interpreter?
2) Or should the above pseudo-code be `if isInFinalState(s) and isScxmlState(s.parent)"`?