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
10
10
)
11
11
}
12
12
13
- fun StateMachine.queuePendingEventHandler (): StateMachine . PendingEventHandler = QueuePendingEventHandler (this )
13
+ fun StateMachine.queuePendingEventHandler (): QueuePendingEventHandler = QueuePendingEventHandlerImpl (this )
14
14
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 {
16
22
private val queue = ArrayDeque <EventAndArgument <* >>()
17
23
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" }
19
25
20
26
override fun onPendingEvent (pendingEvent : Event , argument : Any? ) {
21
27
machine.log { " $machine queued event $pendingEvent with argument $argument " }
22
28
queue.add(EventAndArgument (pendingEvent, argument))
23
29
}
24
30
25
- fun nextEventAndArgument () = queue.removeFirstOrNull()
31
+ override fun nextEventAndArgument () = queue.removeFirstOrNull()
26
32
27
- fun clear () = queue.clear()
33
+ override fun clear () = queue.clear()
28
34
}
Original file line number Diff line number Diff line change @@ -23,7 +23,7 @@ internal class StateMachineImpl(
23
23
override val machineListeners: Collection <StateMachine .Listener > get() = _machineListeners
24
24
override var logger: StateMachine .Logger = NullLogger
25
25
override var ignoredEventHandler = StateMachine .IgnoredEventHandler { _, _ -> }
26
- override var pendingEventHandler = queuePendingEventHandler()
26
+ override var pendingEventHandler: StateMachine . PendingEventHandler = queuePendingEventHandler()
27
27
override var listenerExceptionHandler = StateMachine .ListenerExceptionHandler { throw it }
28
28
private var _isDestroyed : Boolean = false
29
29
override val isDestroyed get() = _isDestroyed
@@ -165,6 +165,10 @@ internal class StateMachineImpl(
165
165
queue?.let {
166
166
var eventAndArgument = it.nextEventAndArgument()
167
167
while (eventAndArgument != null ) {
168
+ if (isDestroyed || ! isRunning) { // if it happens while event processing
169
+ it.clear()
170
+ return result
171
+ }
168
172
process(eventAndArgument)
169
173
170
174
eventAndArgument = it.nextEventAndArgument()
@@ -251,9 +255,11 @@ internal class StateMachineImpl(
251
255
}
252
256
253
257
override fun destroy (stop : Boolean ) {
258
+ if (_isDestroyed ) return
254
259
if (stop) stop()
255
260
accept(CleanupVisitor ())
256
261
_isDestroyed = true
262
+ log { " $this destroyed" }
257
263
}
258
264
}
259
265
Original file line number Diff line number Diff line change @@ -88,4 +88,25 @@ class FinishedEventTest : StringSpec({
88
88
callbacks.onTriggeredTransition(ofType<FinishedDataEvent <Int >>())
89
89
}
90
90
}
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
+ }
91
112
})
Original file line number Diff line number Diff line change @@ -98,4 +98,39 @@ class PendingEventHandlerTest : StringSpec({
98
98
machine.processEvent(SwitchEvent )
99
99
machine.isDestroyed shouldBe false
100
100
}
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
+ }
101
136
})
You can’t perform that action at this time.
0 commit comments