@@ -132,14 +132,15 @@ internal class StateMachineImpl(
132
132
private suspend fun doStartFrom (event : StartEvent , argument : Any? ): Unit =
133
133
coroutineAbstraction.withContext {
134
134
checkBeforeRunMachine()
135
- // fixme loosing this params (but similary (not same target) will be recreated on transition)
135
+ // fixme loosing this params (but similar (not same target) will be recreated on transition)
136
136
val eventAndArgument = EventAndArgument (event, argument)
137
137
eventProcessingScope {
138
- runCheckingExceptions {
138
+ val step1Result = runCheckingExceptions {
139
139
val transitionParams = makeStartTransitionParams(event, this , event.startState, argument)
140
140
runMachine(transitionParams)
141
- doProcessEvent (eventAndArgument)
141
+ processStep1 (eventAndArgument)
142
142
}
143
+ processStep2(eventAndArgument, step1Result)
143
144
}
144
145
}
145
146
@@ -154,7 +155,7 @@ internal class StateMachineImpl(
154
155
requireInitialState()
155
156
}
156
157
157
- /* * To be called only from [runCheckingExceptions] */
158
+ /* * Should be called only from [runCheckingExceptions] */
158
159
private suspend fun runMachine (transitionParams : TransitionParams <* >) {
159
160
_isRunning = true
160
161
_hasProcessedEvents = false
@@ -163,7 +164,7 @@ internal class StateMachineImpl(
163
164
doEnter(transitionParams)
164
165
}
165
166
166
- /* * To be called only from [runCheckingExceptions] */
167
+ /* * Should be called only from [runCheckingExceptions] */
167
168
private suspend fun doStop () {
168
169
_isRunning = false
169
170
recursiveStop()
@@ -202,33 +203,52 @@ internal class StateMachineImpl(
202
203
}
203
204
204
205
private suspend fun process (eventAndArgument : EventAndArgument <* >): ProcessingResult {
205
- if (eventAndArgument.event !is StartEvent )
206
- _hasProcessedEvents = true
206
+ val step1Result = runCheckingExceptions {
207
+ processStep1(eventAndArgument)
208
+ }
209
+ return processStep2(eventAndArgument, step1Result)
210
+ }
207
211
208
- _eventRecorder ?.onProcessEvent(eventAndArgument) // should be done before wrapping to record not wrapped event
212
+ /* *
213
+ * Should be called only from [runCheckingExceptions]
214
+ */
215
+ private suspend fun processStep1 (eventAndArgument : EventAndArgument <* >): Step1Result {
216
+ _eventRecorder ?.onProcessEvent(eventAndArgument) // should be called with not wrapped event
209
217
210
218
val wrappedEventAndArgument = eventAndArgument.wrap()
219
+ val eventProcessed = when (val event = wrappedEventAndArgument.event) {
220
+ is StopEvent -> {
221
+ doStop()
222
+ true
223
+ }
211
224
212
- val eventProcessed = runCheckingExceptions {
213
- when (val event = wrappedEventAndArgument.event) {
214
- is StopEvent -> {
215
- doStop()
216
- true
217
- }
218
- is DestroyEvent -> {
219
- if (event.stop && isRunning) doStop()
220
- doDestroy()
221
- true
222
- }
223
- else -> doProcessEvent(wrappedEventAndArgument)
225
+ is DestroyEvent -> {
226
+ if (event.stop && isRunning) doStop()
227
+ doDestroy()
228
+ true
224
229
}
230
+
231
+ else -> doProcessEvent(wrappedEventAndArgument)
225
232
}
233
+ return Step1Result (eventProcessed, wrappedEventAndArgument)
234
+ }
226
235
227
- if (! eventProcessed) {
228
- log { " $this ignored ${wrappedEventAndArgument.event::class .simpleName} " }
229
- ignoredEventHandler.onIgnoredEvent(wrappedEventAndArgument)
236
+ /* *
237
+ * Possible exceptions from this step should reach the user (caller)
238
+ */
239
+ private suspend fun processStep2 (
240
+ eventAndArgument : EventAndArgument <* >,
241
+ step1Result : Step1Result ,
242
+ ): ProcessingResult {
243
+ return if (step1Result.eventProcessed) {
244
+ if (eventAndArgument.event !is StartEvent )
245
+ _hasProcessedEvents = true
246
+ ProcessingResult .PROCESSED
247
+ } else {
248
+ log { " $this ignored ${step1Result.wrappedEventAndArgument.event::class .simpleName} " }
249
+ ignoredEventHandler.onIgnoredEvent(step1Result.wrappedEventAndArgument)
250
+ ProcessingResult .IGNORED
230
251
}
231
- return if (eventProcessed) ProcessingResult .PROCESSED else ProcessingResult .IGNORED
232
252
}
233
253
234
254
/* *
@@ -266,7 +286,11 @@ internal class StateMachineImpl(
266
286
}
267
287
268
288
/* *
269
- * Runs block of code that triggers notification listeners
289
+ * Runs block of code that internally triggers notification listeners.
290
+ *
291
+ * As listeners exceptions are delayed and may be rethrown to a caller (user) and this should not cause machine
292
+ * destruction.
293
+ * Watch [runCheckingExceptions] blocks are not nested it is not supported and wrong.
270
294
*/
271
295
private suspend fun <R > runCheckingExceptions (block : suspend () -> R ): R {
272
296
val result: R
@@ -284,6 +308,9 @@ internal class StateMachineImpl(
284
308
return result
285
309
}
286
310
311
+ /* *
312
+ * @return true if event was processed (transition was performed), false if it was ignored
313
+ */
287
314
private suspend fun <E : Event > doProcessEvent (eventAndArgument : EventAndArgument <E >): Boolean {
288
315
val (event, argument) = eventAndArgument
289
316
if (isFinished) {
@@ -326,15 +353,15 @@ internal class StateMachineImpl(
326
353
* Starts machine if it is inner state of another one machine
327
354
*/
328
355
override suspend fun doEnter (transitionParams : TransitionParams <* >) {
329
- if (! isRunning) startBlocking () else super .doEnter(transitionParams)
356
+ if (! isRunning) start () else super .doEnter(transitionParams)
330
357
}
331
358
332
359
override suspend fun cleanup () {
333
360
_machineListeners .clear()
334
361
super .cleanup()
335
362
}
336
363
337
- /* * To be called only from [runCheckingExceptions] */
364
+ /* * Should be called only from [runCheckingExceptions] */
338
365
private suspend fun doDestroy () {
339
366
_isDestroyed = true
340
367
machineNotify { onDestroyed() }
@@ -345,6 +372,9 @@ internal class StateMachineImpl(
345
372
346
373
internal fun StateMachine.checkNotDestroyed () = check(! isDestroyed) { " $this is already destroyed" }
347
374
375
+ /* *
376
+ * Method should be used for running code that sends notifications for outer world (calls listeners)
377
+ */
348
378
internal suspend inline fun InternalStateMachine.runDelayingException (crossinline block : suspend () -> Unit ) =
349
379
try {
350
380
block()
@@ -376,4 +406,9 @@ internal suspend inline fun <reified E : StartEvent> makeStartTransitionParams(
376
406
}
377
407
378
408
private fun StateMachine.checkPropertyNotMutedOnRunningMachine (propertyType : KClass <* >) =
379
- check(! isRunning) { " Can not change ${propertyType.simpleName} after state machine started" }
409
+ check(! isRunning) { " Can not change ${propertyType.simpleName} after state machine started" }
410
+
411
+ private data class Step1Result (
412
+ val eventProcessed : Boolean ,
413
+ val wrappedEventAndArgument : EventAndArgument <* >,
414
+ )
0 commit comments