mirror of
https://github.com/codex-storage/nim-codex.git
synced 2025-02-26 19:40:40 +00:00
[marketplace] add async state machine
Allows for `enterAsync` to be cancelled.
This commit is contained in:
parent
88220ddaae
commit
7bb874c998
@ -1,3 +1,5 @@
|
|||||||
|
import std/typetraits
|
||||||
|
import pkg/chronicles
|
||||||
import pkg/questionable
|
import pkg/questionable
|
||||||
import pkg/chronos
|
import pkg/chronos
|
||||||
import ./optionalcast
|
import ./optionalcast
|
||||||
@ -62,6 +64,9 @@ type
|
|||||||
State* = ref object of RootObj
|
State* = ref object of RootObj
|
||||||
context: ?StateMachine
|
context: ?StateMachine
|
||||||
|
|
||||||
|
method `$`*(state: State): string {.base.} =
|
||||||
|
(typeof state).name
|
||||||
|
|
||||||
method enter(state: State) {.base.} =
|
method enter(state: State) {.base.} =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
@ -88,6 +93,8 @@ proc switch*(oldState, newState: State) =
|
|||||||
|
|
||||||
type
|
type
|
||||||
AsyncState* = ref object of State
|
AsyncState* = ref object of State
|
||||||
|
activeTransition: ?Future[void]
|
||||||
|
StateMachineAsync* = ref object of StateMachine
|
||||||
|
|
||||||
method enterAsync(state: AsyncState) {.base, async.} =
|
method enterAsync(state: AsyncState) {.base, async.} =
|
||||||
discard
|
discard
|
||||||
@ -100,3 +107,26 @@ method enter(state: AsyncState) =
|
|||||||
|
|
||||||
method exit(state: AsyncState) =
|
method exit(state: AsyncState) =
|
||||||
asyncSpawn state.exitAsync()
|
asyncSpawn state.exitAsync()
|
||||||
|
|
||||||
|
proc switchAsync*(machine: StateMachineAsync, newState: AsyncState) {.async.} =
|
||||||
|
if state =? (machine.state as AsyncState):
|
||||||
|
trace "Switching sales state", `from`=state, to=newState
|
||||||
|
debugEcho "switching from ", state, " to ", newState
|
||||||
|
if activeTransition =? state.activeTransition and
|
||||||
|
not activeTransition.completed:
|
||||||
|
await activeTransition.cancelAndWait()
|
||||||
|
# should wait for exit before switch. could add a transition option during
|
||||||
|
# switch if we don't need to wait
|
||||||
|
await state.exitAsync()
|
||||||
|
state.context = none StateMachine
|
||||||
|
else:
|
||||||
|
trace "Switching sales state", `from`="no state", to=newState
|
||||||
|
debugEcho "switching from no state to ", newState
|
||||||
|
|
||||||
|
machine.state = some State(newState)
|
||||||
|
newState.context = some StateMachine(machine)
|
||||||
|
newState.activeTransition = some newState.enterAsync()
|
||||||
|
|
||||||
|
proc switchAsync*(oldState, newState: AsyncState) {.async.} =
|
||||||
|
if context =? oldState.context:
|
||||||
|
await StateMachineAsync(context).switchAsync(newState)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import ./utils/teststatemachine
|
import ./utils/teststatemachine
|
||||||
|
import ./utils/teststatemachineasync
|
||||||
import ./utils/testoptionalcast
|
import ./utils/testoptionalcast
|
||||||
import ./utils/testkeyutils
|
import ./utils/testkeyutils
|
||||||
|
|
||||||
|
30
tests/codex/utils/teststatemachineasync.nim
Normal file
30
tests/codex/utils/teststatemachineasync.nim
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import pkg/asynctest
|
||||||
|
import pkg/chronos
|
||||||
|
import pkg/questionable
|
||||||
|
import codex/utils/statemachine
|
||||||
|
|
||||||
|
type
|
||||||
|
AsyncMachine = ref object of StateMachineAsync
|
||||||
|
LongRunningStart = ref object of AsyncState
|
||||||
|
LongRunningFinish = ref object of AsyncState
|
||||||
|
LongRunningError = ref object of AsyncState
|
||||||
|
Callback = proc(): Future[void] {.gcsafe.}
|
||||||
|
|
||||||
|
proc triggerIn(time: Duration, cb: Callback) {.async.} =
|
||||||
|
await sleepAsync(time)
|
||||||
|
await cb()
|
||||||
|
|
||||||
|
method enterAsync(state: LongRunningStart) {.async.} =
|
||||||
|
proc cb() {.async.} =
|
||||||
|
await state.switchAsync(LongRunningFinish())
|
||||||
|
asyncSpawn triggerIn(500.milliseconds, cb)
|
||||||
|
await sleepAsync(1.seconds)
|
||||||
|
await state.switchAsync(LongRunningError())
|
||||||
|
|
||||||
|
suite "async state machines":
|
||||||
|
|
||||||
|
test "can cancel a state":
|
||||||
|
let am = AsyncMachine()
|
||||||
|
await am.switchAsync(LongRunningStart())
|
||||||
|
await sleepAsync(2.seconds)
|
||||||
|
check (am.state as LongRunningFinish).isSome
|
Loading…
x
Reference in New Issue
Block a user