[statemachine] states and events should not raise errors

They would crash the scheduler loop.
This commit is contained in:
Mark Spanbroek 2023-02-13 16:24:31 +01:00
parent f6b534f1e1
commit 50130c33f5
No known key found for this signature in database
GPG Key ID: FBE3E9548D427C00
2 changed files with 13 additions and 9 deletions

View File

@ -1,5 +1,6 @@
import pkg/questionable
import pkg/chronos
import pkg/upraises
template makeStateMachine*(MachineType, StateType) =
@ -10,7 +11,7 @@ template makeStateMachine*(MachineType, StateType) =
scheduled: AsyncQueue[Event]
scheduling: Future[void]
StateType* = ref object of RootObj
Event = proc(state: StateType): ?StateType {.gcsafe.}
Event = proc(state: StateType): ?StateType {.gcsafe, upraises:[].}
proc transition(_: type Event, previous, next: StateType): Event =
return proc (state: StateType): ?StateType =
@ -20,7 +21,7 @@ template makeStateMachine*(MachineType, StateType) =
proc schedule*(machine: MachineType, event: Event) =
machine.scheduled.putNoWait(event)
method run*(state: StateType): Future[?StateType] {.base.} =
method run*(state: StateType): Future[?StateType] {.base, upraises:[].} =
discard
proc run(machine: MachineType, state: StateType) {.async.} =

View File

@ -1,7 +1,7 @@
import std/sugar
import pkg/asynctest
import pkg/questionable
import pkg/chronos
import pkg/upraises
import codex/utils/asyncstatemachine
import ../helpers/eventually
@ -14,7 +14,7 @@ type
var runs, cancellations = [0, 0, 0]
method onMoveToNextStateEvent*(state: State): ?State {.base.} =
method onMoveToNextStateEvent*(state: State): ?State {.base, upraises:[].} =
discard
method run(state: State1): Future[?State] {.async.} =
@ -41,6 +41,9 @@ suite "async state machines":
var machine: Machine
var state1, state2: State
proc moveToNextStateEvent(state: State): ?State =
state.onMoveToNextStateEvent()
setup:
runs = [0, 0, 0]
cancellations = [0, 0, 0]
@ -58,25 +61,25 @@ suite "async state machines":
test "state2 moves to state3 on event":
machine.start(state2)
machine.schedule(state {.gcsafe.} => state.onMoveToNextStateEvent())
machine.schedule(moveToNextStateEvent)
check eventually runs == [0, 1, 1]
test "state transition will cancel the running state":
machine.start(state2)
machine.schedule(state {.gcsafe.} => state.onMoveToNextStateEvent())
machine.schedule(moveToNextStateEvent)
check eventually cancellations == [0, 1, 0]
test "scheduled events are handled one after the other":
machine.start(state2)
machine.schedule(state {.gcsafe.} => state.onMoveToNextStateEvent())
machine.schedule(state {.gcsafe.} => state.onMoveToNextStateEvent())
machine.schedule(moveToNextStateEvent)
machine.schedule(moveToNextStateEvent)
check eventually runs == [1, 2, 1]
test "stops scheduling and current state":
machine.start(state2)
await sleepAsync(1.millis)
machine.stop()
machine.schedule(state {.gcsafe.} => state.onMoveToNextStateEvent())
machine.schedule(moveToNextStateEvent)
await sleepAsync(1.millis)
check runs == [0, 1, 0]
check cancellations == [0, 1, 0]