File tree Expand file tree Collapse file tree 4 files changed +74
-6
lines changed
main/kotlin/ru/nsk/kstatemachine
test/kotlin/ru/nsk/kstatemachine Expand file tree Collapse file tree 4 files changed +74
-6
lines changed Original file line number Diff line number Diff line change @@ -10,19 +10,25 @@ fun StateMachine.throwingPendingEventHandler() = StateMachine.PendingEventHandle
1010 )
1111}
1212
13- fun StateMachine.queuePendingEventHandler (): StateMachine . PendingEventHandler = QueuePendingEventHandler (this )
13+ fun StateMachine.queuePendingEventHandler (): QueuePendingEventHandler = QueuePendingEventHandlerImpl (this )
1414
15- internal class QueuePendingEventHandler (private val machine : StateMachine ) : StateMachine.PendingEventHandler {
15+ interface QueuePendingEventHandler : StateMachine .PendingEventHandler {
16+ fun checkEmpty ()
17+ fun nextEventAndArgument (): EventAndArgument <* >?
18+ fun clear ()
19+ }
20+
21+ private class QueuePendingEventHandlerImpl (private val machine : StateMachine ) : QueuePendingEventHandler {
1622 private val queue = ArrayDeque <EventAndArgument <* >>()
1723
18- fun checkEmpty () = check(queue.isEmpty()) { " Event queue is not empty, internal error" }
24+ override fun checkEmpty () = check(queue.isEmpty()) { " Event queue is not empty, internal error" }
1925
2026 override fun onPendingEvent (pendingEvent : Event , argument : Any? ) {
2127 machine.log { " $machine queued event $pendingEvent with argument $argument " }
2228 queue.add(EventAndArgument (pendingEvent, argument))
2329 }
2430
25- fun nextEventAndArgument () = queue.removeFirstOrNull()
31+ override fun nextEventAndArgument () = queue.removeFirstOrNull()
2632
27- fun clear () = queue.clear()
33+ override fun clear () = queue.clear()
2834}
Original file line number Diff line number Diff line change @@ -23,7 +23,7 @@ internal class StateMachineImpl(
2323 override val machineListeners: Collection <StateMachine .Listener > get() = _machineListeners
2424 override var logger: StateMachine .Logger = NullLogger
2525 override var ignoredEventHandler = StateMachine .IgnoredEventHandler { _, _ -> }
26- override var pendingEventHandler = queuePendingEventHandler()
26+ override var pendingEventHandler: StateMachine . PendingEventHandler = queuePendingEventHandler()
2727 override var listenerExceptionHandler = StateMachine .ListenerExceptionHandler { throw it }
2828 private var _isDestroyed : Boolean = false
2929 override val isDestroyed get() = _isDestroyed
@@ -165,6 +165,10 @@ internal class StateMachineImpl(
165165 queue?.let {
166166 var eventAndArgument = it.nextEventAndArgument()
167167 while (eventAndArgument != null ) {
168+ if (isDestroyed || ! isRunning) { // if it happens while event processing
169+ it.clear()
170+ return result
171+ }
168172 process(eventAndArgument)
169173
170174 eventAndArgument = it.nextEventAndArgument()
@@ -251,9 +255,11 @@ internal class StateMachineImpl(
251255 }
252256
253257 override fun destroy (stop : Boolean ) {
258+ if (_isDestroyed ) return
254259 if (stop) stop()
255260 accept(CleanupVisitor ())
256261 _isDestroyed = true
262+ log { " $this destroyed" }
257263 }
258264}
259265
Original file line number Diff line number Diff line change @@ -88,4 +88,25 @@ class FinishedEventTest : StringSpec({
8888 callbacks.onTriggeredTransition(ofType<FinishedDataEvent <Int >>())
8989 }
9090 }
91+
92+ " FinishedEvent in parallel child mode" {
93+ val callbacks = mockkCallbacks()
94+
95+ createStateMachine {
96+ initialState(childMode = ChildMode .PARALLEL ) {
97+ state("state1") {
98+ setInitialState(finalState("state11"))
99+ }
100+ state("state2") {
101+ setInitialState(finalState("state21"))
102+ }
103+ transition<FinishedEvent > {
104+ callbacks.listen(this)
105+ }
106+ }
107+ }
108+ verifySequence {
109+ callbacks.onTriggeredTransition(ofType<FinishedEvent >())
110+ }
111+ }
91112})
Original file line number Diff line number Diff line change @@ -98,4 +98,39 @@ class PendingEventHandlerTest : StringSpec({
9898 machine.processEvent(SwitchEvent )
9999 machine.isDestroyed shouldBe false
100100 }
101+
102+ " pending events are cleared on stop() from notification callback" {
103+ val machine = createStateMachine {
104+ val state2 = state("state2") {
105+ onEntry {
106+ machine.processEvent(SwitchEvent ) shouldBe PENDING
107+ machine.processEvent(SwitchEvent ) shouldBe PENDING
108+ machine.stop()
109+ }
110+ }
111+ initialState("state1") {
112+ transition<SwitchEvent >(targetState = state2)
113+ }
114+ }
115+ machine.processEvent(SwitchEvent )
116+ machine.isRunning shouldBe false
117+ machine.start()
118+ }
119+
120+ " pending events are cleared on destroy() from notification callback" {
121+ val machine = createStateMachine {
122+ val state2 = state("state2") {
123+ onEntry {
124+ machine.processEvent(SwitchEvent ) shouldBe PENDING
125+ machine.processEvent(SwitchEvent ) shouldBe PENDING
126+ machine.destroy(false)
127+ }
128+ }
129+ initialState("state1") {
130+ transition<SwitchEvent >(targetState = state2)
131+ }
132+ }
133+ machine.processEvent(SwitchEvent )
134+ machine.isDestroyed shouldBe true
135+ }
101136})
You can’t perform that action at this time.
0 commit comments