You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
3.3 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. package v2
  2. import (
  3. "fmt"
  4. "sync/atomic"
  5. "github.com/Workiva/go-datastructures/queue"
  6. "github.com/tendermint/tendermint/libs/log"
  7. )
  8. // TODO
  9. // * revisit panic conditions
  10. // * audit log levels
  11. // * Convert routine to an interface with concrete implmentation
  12. type handleFunc = func(event Event) (Event, error)
  13. // Routines are a structure which model a finite state machine as serialized
  14. // stream of events processed by a handle function. This Routine structure
  15. // handles the concurrency and messaging guarantees. Events are sent via
  16. // `trySend` are handled by the `handle` function to produce an iterator
  17. // `next()`. Calling `close()` on a routine will conclude processing of all
  18. // sent events and produce `final()` event representing the terminal state.
  19. type Routine struct {
  20. name string
  21. handle handleFunc
  22. queue *queue.PriorityQueue
  23. out chan Event
  24. fin chan error
  25. rdy chan struct{}
  26. running *uint32
  27. logger log.Logger
  28. metrics *Metrics
  29. }
  30. var queueSize int = 10
  31. func newRoutine(name string, handleFunc handleFunc) *Routine {
  32. return &Routine{
  33. name: name,
  34. handle: handleFunc,
  35. queue: queue.NewPriorityQueue(queueSize, true),
  36. out: make(chan Event, queueSize),
  37. rdy: make(chan struct{}, 1),
  38. fin: make(chan error, 1),
  39. running: new(uint32),
  40. logger: log.NewNopLogger(),
  41. metrics: NopMetrics(),
  42. }
  43. }
  44. func (rt *Routine) setLogger(logger log.Logger) {
  45. rt.logger = logger
  46. }
  47. // nolint:unused
  48. func (rt *Routine) setMetrics(metrics *Metrics) {
  49. rt.metrics = metrics
  50. }
  51. func (rt *Routine) start() {
  52. rt.logger.Info(fmt.Sprintf("%s: run\n", rt.name))
  53. running := atomic.CompareAndSwapUint32(rt.running, uint32(0), uint32(1))
  54. if !running {
  55. panic(fmt.Sprintf("%s is already running", rt.name))
  56. }
  57. close(rt.rdy)
  58. defer func() {
  59. stopped := atomic.CompareAndSwapUint32(rt.running, uint32(1), uint32(0))
  60. if !stopped {
  61. panic(fmt.Sprintf("%s is failed to stop", rt.name))
  62. }
  63. }()
  64. for {
  65. events, err := rt.queue.Get(1)
  66. if err != nil {
  67. rt.logger.Info(fmt.Sprintf("%s: stopping\n", rt.name))
  68. rt.terminate(fmt.Errorf("stopped"))
  69. return
  70. }
  71. oEvent, err := rt.handle(events[0])
  72. rt.metrics.EventsHandled.With("routine", rt.name).Add(1)
  73. if err != nil {
  74. rt.terminate(err)
  75. return
  76. }
  77. rt.metrics.EventsOut.With("routine", rt.name).Add(1)
  78. rt.logger.Debug(fmt.Sprintf("%s produced %+v event\n", rt.name, oEvent))
  79. rt.out <- oEvent
  80. }
  81. }
  82. func (rt *Routine) trySend(event Event) bool {
  83. rt.logger.Info(fmt.Sprintf("%s: sending %+v", rt.name, event))
  84. if !rt.isRunning() {
  85. return false
  86. }
  87. err := rt.queue.Put(event)
  88. if err != nil {
  89. rt.metrics.EventsShed.With("routine", rt.name).Add(1)
  90. rt.logger.Info(fmt.Sprintf("%s: trySend fail, queue was full/stopped \n", rt.name))
  91. return false
  92. }
  93. rt.metrics.EventsSent.With("routine", rt.name).Add(1)
  94. return true
  95. }
  96. func (rt *Routine) isRunning() bool {
  97. return atomic.LoadUint32(rt.running) == 1
  98. }
  99. func (rt *Routine) next() chan Event {
  100. return rt.out
  101. }
  102. func (rt *Routine) ready() chan struct{} {
  103. return rt.rdy
  104. }
  105. func (rt *Routine) stop() {
  106. if !rt.isRunning() {
  107. return
  108. }
  109. rt.logger.Info(fmt.Sprintf("%s: stop\n", rt.name))
  110. rt.queue.Dispose() // this should block until all queue items are free?
  111. }
  112. func (rt *Routine) final() chan error {
  113. return rt.fin
  114. }
  115. // XXX: Maybe get rid of this
  116. func (rt *Routine) terminate(reason error) {
  117. close(rt.out)
  118. rt.fin <- reason
  119. }