Nested final states in parallel won't raise done event
sradomski opened this issue · comments
When every child of a parallel is in a final basic configuration, the interpreter ought to raise a done.state
event for the parallel state. In LXSC, this is only the case if the final states are direct children of the parallel state.
Thanks for the test! I'll see what I call do.
If your test case had the <final>
as a direct child of direct children of the <parallel>
, I can justify it with the standard. As you say, the following testcase passes today:
<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" seed="639855585" name="finalParallel" version="1.0">
<parallel id="p0">
<onentry><send event="timeout" delay="1s"/></onentry>
<state id="p0_s0"><final id="p0_s0_f0"/></state>
<state id="p0_s1"><final id="p0_s1_f0"/></state>
<transition target="pass" event="done.state.p0"/>
<transition target="fail" event="timeout"/>
</parallel>
<final id="pass" />
<final id="fail" />
</scxml>
Section 3.7 of the spec says:
When the state machine enters the
<final>
child of a<state>
element […] generate the eventdone.state.id
[...] whereid
is the id of the parent state. Immediately thereafter, if the parent<state>
is a child of a<parallel>
element, and all […] other children are also in final states […] generate the eventdone.state.id
whereid
is the id of the<parallel>
element.
Note the highlighted words "parent"/"child" above (not "ancestor"/"descendant").
I'm looking for normative prose that says ~ "When done.state.id
is fired on a <state>
—as a result of the machine entering a <final>
state—if the parent of the <state>
is itself a <state>
, that parent state shall itself have done.state.id
fired upon it." (Except that specific prose I wrote is fraught with danger, as it could be interpreted to mean that entering a <final>
state in one part of a state machine and manually an event named done.state.foo
on an unrelated <state>
should trigger the cascade of events.) Do you know where such a spec might exist? Is there a mistake in the spec that does not explicitly cover the 'done'-ness propogating upwards?
Currently on your test file done.state.id2
and done.state.id5
are fired by the LXSC interpreter. Do you think that done.state.id1
and done.state.id4
be fired, before done.state.p0
is fired?
Further discussion: in the (informative, not normative) Appendix D of the spec we have the following pseudo-code at the end of enterStates()
:
if isFinalState(s):
if isSCXMLElement(s.parent):
running = false
else:
parent = s.parent
grandparent = parent.parent
internalQueue.enqueue(new Event("done.state." + parent.id, s.donedata))
if isParallelState(grandparent):
if getChildStates(grandparent).every(isInFinalState):
internalQueue.enqueue(new Event("done.state." + grandparent.id))
The above does not propagate the done.state.
event up the ancestry tree below a <parallel>
. Further, we have this pseudo-code for the isInFinalState()
determination:
function isInFinalState(s):
if isCompoundState(s):
return getChildStates(s).some(lambda s: isFinalState(s) and configuration.isMember(s))
elif isParallelState(s):
return getChildStates(s).every(isInFinalState)
else:
return false
Again, the above only iterates the children of a compound state and looks to see if they are final states, without recursively calling isInFinalState()
on each child.
I'm guessing that this all is an oversight, but without spec or errata to back up the change, I'm hesitant to change the interpreter to behave as I/we think it ought to behave, when that seems to conflict with the specification.
Yes, not having an interpretable Appendix D has been a major source of criticism. But as far as I can tell, "done.state." + grandparent.id
is raised if isInFinalState
holds for all of its children. In the case of compounds as in the test given, that is true if its active child state is a final state. That is strangely specific and I would challenge the implied semantic if it was not already a recommendation.
But granted, the spec does say something different. However, under a surprise me the least aspect, you'd expect done.state.[parallel@id]
to be raised when all its active child states are final. At least that is what our users expected and what we implemented.
I'm going to close this for now due to the reasonable comments by Jim in this thread:
http://lists.w3.org/Archives/Public/www-voice/2017AprJun/thread.html
If you can show me that most other interpreters behave as yours does, I'll likely move that way for consistency. But for now, I'll stick to the de jure spec in lieu of a de facto one.