fsm2.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. package fsm
  2. import (
  3. "context"
  4. "time"
  5. )
  6. type M2Hook[T any] func(ctx context.Context, event M2Event[T]) error
  7. type M2State[T any] interface {
  8. Name() string
  9. SetAfterEnter([]M2Hook[T])
  10. AfterEnter() []M2Hook[T]
  11. SetBeforeLeave([]M2Hook[T])
  12. BeforeLeave() []M2Hook[T]
  13. }
  14. type M2Event[T any] interface {
  15. Name() string
  16. Data() T
  17. }
  18. type RetryableError interface {
  19. error
  20. RetryableError()
  21. }
  22. type M2Transition[T any] struct {
  23. guards, actions []M2Hook[T]
  24. next State
  25. }
  26. func (mt *M2Transition[T]) SetGuards([]M2Hook[T]) {}
  27. func (mt *M2Transition[T]) Guards() []M2Hook[T] { return mt.guards }
  28. func (mt *M2Transition[T]) SetActions([]M2Hook[T]) {}
  29. func (mt *M2Transition[T]) Actions() []M2Hook[T] { return mt.actions }
  30. func (mt *M2Transition[T]) Next() State { return mt.next }
  31. type M2Matrix[T any] map[M2State[T]]map[M2Event[T]]M2Transition[T]
  32. // M2BackoffFunc returning <0 means the retry limit has been reached.
  33. type M2BackoffFunc func() time.Duration
  34. type M2FSM[T any] interface {
  35. Name() string
  36. SetBackoff(fn M2BackoffFunc)
  37. SetMatrix(mx M2Matrix[T])
  38. Matrix() M2Matrix[T]
  39. StartState() M2State[T]
  40. EndState() M2State[T]
  41. ErrorState() M2State[T]
  42. }
  43. type M2PushMachine[T any] interface {
  44. M2FSM[T]
  45. Handle(context.Context, M2Event[T]) error // If ctx == nil, handling uses the machine context.
  46. }
  47. type M2PullMachine[T any] interface {
  48. M2FSM[T]
  49. Start(ctx context.Context, events chan<- M2Event[T]) M2State[T]
  50. }
  51. type M2VerifiableMachine[T any] interface {
  52. M2FSM[T]
  53. IsReachable(s1, s2 M2State[T]) bool
  54. IsStronglyConnected() bool // Minimal Complexity O(V+E) using DFS (Tarjan or Kosaraju)
  55. }
  56. var (
  57. m2BackoffInitialDelay = time.Millisecond
  58. m2BackoffMaxAttempts = 10
  59. m2DefaultBackoffAttempts = 0
  60. // M2YoloBackoff retries indefinitely without delay.
  61. M2YoloBackoff M2BackoffFunc = func() time.Duration {
  62. return 0
  63. }
  64. // M2NoBackoff retries immediately but only up to the maximum number of retries.
  65. M2NoBackoff M2BackoffFunc = func() time.Duration {
  66. m2DefaultBackoffAttempts++
  67. if m2DefaultBackoffAttempts > m2BackoffMaxAttempts {
  68. return -1
  69. }
  70. return M2YoloBackoff()
  71. }
  72. // M2ExponentialBackoff retries with an exponential delay and maximum number of retries.
  73. M2ExponentialBackoff M2BackoffFunc = func() time.Duration {
  74. d := M2NoBackoff()
  75. if d < 0 {
  76. return d
  77. }
  78. return (1 << m2DefaultBackoffAttempts) * m2BackoffInitialDelay
  79. }
  80. )