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.

194 lines
4.5 KiB

  1. // Package events - Pub-Sub in go with event caching
  2. package events
  3. import (
  4. "context"
  5. "fmt"
  6. "sync"
  7. "github.com/tendermint/tendermint/libs/log"
  8. )
  9. // ErrListenerWasRemoved is returned by AddEvent if the listener was removed.
  10. type ErrListenerWasRemoved struct {
  11. listenerID string
  12. }
  13. // Error implements the error interface.
  14. func (e ErrListenerWasRemoved) Error() string {
  15. return fmt.Sprintf("listener #%s was removed", e.listenerID)
  16. }
  17. // EventData is a generic event data can be typed and registered with
  18. // tendermint/go-amino via concrete implementation of this interface.
  19. type EventData interface{}
  20. // Eventable is the interface reactors and other modules must export to become
  21. // eventable.
  22. type Eventable interface {
  23. SetEventSwitch(evsw EventSwitch)
  24. }
  25. // Fireable is the interface that wraps the FireEvent method.
  26. //
  27. // FireEvent fires an event with the given name and data.
  28. type Fireable interface {
  29. FireEvent(ctx context.Context, eventValue string, data EventData)
  30. }
  31. // EventSwitch is the interface for synchronous pubsub, where listeners
  32. // subscribe to certain events and, when an event is fired (see Fireable),
  33. // notified via a callback function.
  34. //
  35. // Listeners are added by calling AddListenerForEvent function.
  36. // They can be removed by calling either RemoveListenerForEvent or
  37. // RemoveListener (for all events).
  38. type EventSwitch interface {
  39. Fireable
  40. AddListenerForEvent(listenerID, eventValue string, cb EventCallback) error
  41. }
  42. type eventSwitch struct {
  43. mtx sync.RWMutex
  44. eventCells map[string]*eventCell
  45. listeners map[string]*eventListener
  46. }
  47. func NewEventSwitch(logger log.Logger) EventSwitch {
  48. evsw := &eventSwitch{
  49. eventCells: make(map[string]*eventCell),
  50. listeners: make(map[string]*eventListener),
  51. }
  52. return evsw
  53. }
  54. func (evsw *eventSwitch) AddListenerForEvent(listenerID, eventValue string, cb EventCallback) error {
  55. // Get/Create eventCell and listener.
  56. evsw.mtx.Lock()
  57. eventCell := evsw.eventCells[eventValue]
  58. if eventCell == nil {
  59. eventCell = newEventCell()
  60. evsw.eventCells[eventValue] = eventCell
  61. }
  62. listener := evsw.listeners[listenerID]
  63. if listener == nil {
  64. listener = newEventListener(listenerID)
  65. evsw.listeners[listenerID] = listener
  66. }
  67. evsw.mtx.Unlock()
  68. if err := listener.AddEvent(eventValue); err != nil {
  69. return err
  70. }
  71. eventCell.AddListener(listenerID, cb)
  72. return nil
  73. }
  74. func (evsw *eventSwitch) FireEvent(ctx context.Context, event string, data EventData) {
  75. // Get the eventCell
  76. evsw.mtx.RLock()
  77. eventCell := evsw.eventCells[event]
  78. evsw.mtx.RUnlock()
  79. if eventCell == nil {
  80. return
  81. }
  82. // Fire event for all listeners in eventCell
  83. eventCell.FireEvent(ctx, data)
  84. }
  85. //-----------------------------------------------------------------------------
  86. // eventCell handles keeping track of listener callbacks for a given event.
  87. type eventCell struct {
  88. mtx sync.RWMutex
  89. listeners map[string]EventCallback
  90. }
  91. func newEventCell() *eventCell {
  92. return &eventCell{
  93. listeners: make(map[string]EventCallback),
  94. }
  95. }
  96. func (cell *eventCell) AddListener(listenerID string, cb EventCallback) {
  97. cell.mtx.Lock()
  98. cell.listeners[listenerID] = cb
  99. cell.mtx.Unlock()
  100. }
  101. func (cell *eventCell) RemoveListener(listenerID string) int {
  102. cell.mtx.Lock()
  103. delete(cell.listeners, listenerID)
  104. numListeners := len(cell.listeners)
  105. cell.mtx.Unlock()
  106. return numListeners
  107. }
  108. func (cell *eventCell) FireEvent(ctx context.Context, data EventData) {
  109. cell.mtx.RLock()
  110. eventCallbacks := make([]EventCallback, 0, len(cell.listeners))
  111. for _, cb := range cell.listeners {
  112. eventCallbacks = append(eventCallbacks, cb)
  113. }
  114. cell.mtx.RUnlock()
  115. for _, cb := range eventCallbacks {
  116. if err := cb(ctx, data); err != nil {
  117. // should we log or abort here?
  118. continue
  119. }
  120. }
  121. }
  122. //-----------------------------------------------------------------------------
  123. type EventCallback func(ctx context.Context, data EventData) error
  124. type eventListener struct {
  125. id string
  126. mtx sync.RWMutex
  127. removed bool
  128. events []string
  129. }
  130. func newEventListener(id string) *eventListener {
  131. return &eventListener{
  132. id: id,
  133. removed: false,
  134. events: nil,
  135. }
  136. }
  137. func (evl *eventListener) AddEvent(event string) error {
  138. evl.mtx.Lock()
  139. if evl.removed {
  140. evl.mtx.Unlock()
  141. return ErrListenerWasRemoved{listenerID: evl.id}
  142. }
  143. evl.events = append(evl.events, event)
  144. evl.mtx.Unlock()
  145. return nil
  146. }
  147. func (evl *eventListener) GetEvents() []string {
  148. evl.mtx.RLock()
  149. events := make([]string, len(evl.events))
  150. copy(events, evl.events)
  151. evl.mtx.RUnlock()
  152. return events
  153. }
  154. func (evl *eventListener) SetRemoved() {
  155. evl.mtx.Lock()
  156. evl.removed = true
  157. evl.mtx.Unlock()
  158. }