github.com/noirx94/tendermintmp@v0.0.1/libs/events/events.go (about) 1 // Package events - Pub-Sub in go with event caching 2 package events 3 4 import ( 5 "fmt" 6 7 "github.com/tendermint/tendermint/libs/service" 8 tmsync "github.com/tendermint/tendermint/libs/sync" 9 ) 10 11 // ErrListenerWasRemoved is returned by AddEvent if the listener was removed. 12 type ErrListenerWasRemoved struct { 13 listenerID string 14 } 15 16 // Error implements the error interface. 17 func (e ErrListenerWasRemoved) Error() string { 18 return fmt.Sprintf("listener #%s was removed", e.listenerID) 19 } 20 21 // EventData is a generic event data can be typed and registered with 22 // tendermint/go-amino via concrete implementation of this interface. 23 type EventData interface{} 24 25 // Eventable is the interface reactors and other modules must export to become 26 // eventable. 27 type Eventable interface { 28 SetEventSwitch(evsw EventSwitch) 29 } 30 31 // Fireable is the interface that wraps the FireEvent method. 32 // 33 // FireEvent fires an event with the given name and data. 34 type Fireable interface { 35 FireEvent(event string, data EventData) 36 } 37 38 // EventSwitch is the interface for synchronous pubsub, where listeners 39 // subscribe to certain events and, when an event is fired (see Fireable), 40 // notified via a callback function. 41 // 42 // Listeners are added by calling AddListenerForEvent function. 43 // They can be removed by calling either RemoveListenerForEvent or 44 // RemoveListener (for all events). 45 type EventSwitch interface { 46 service.Service 47 Fireable 48 49 AddListenerForEvent(listenerID, event string, cb EventCallback) error 50 RemoveListenerForEvent(event string, listenerID string) 51 RemoveListener(listenerID string) 52 } 53 54 type eventSwitch struct { 55 service.BaseService 56 57 mtx tmsync.RWMutex 58 eventCells map[string]*eventCell 59 listeners map[string]*eventListener 60 } 61 62 func NewEventSwitch() EventSwitch { 63 evsw := &eventSwitch{ 64 eventCells: make(map[string]*eventCell), 65 listeners: make(map[string]*eventListener), 66 } 67 evsw.BaseService = *service.NewBaseService(nil, "EventSwitch", evsw) 68 return evsw 69 } 70 71 func (evsw *eventSwitch) OnStart() error { 72 return nil 73 } 74 75 func (evsw *eventSwitch) OnStop() {} 76 77 func (evsw *eventSwitch) AddListenerForEvent(listenerID, event string, cb EventCallback) error { 78 // Get/Create eventCell and listener. 79 evsw.mtx.Lock() 80 eventCell := evsw.eventCells[event] 81 if eventCell == nil { 82 eventCell = newEventCell() 83 evsw.eventCells[event] = eventCell 84 } 85 listener := evsw.listeners[listenerID] 86 if listener == nil { 87 listener = newEventListener(listenerID) 88 evsw.listeners[listenerID] = listener 89 } 90 evsw.mtx.Unlock() 91 92 // Add event and listener. 93 if err := listener.AddEvent(event); err != nil { 94 return err 95 } 96 eventCell.AddListener(listenerID, cb) 97 98 return nil 99 } 100 101 func (evsw *eventSwitch) RemoveListener(listenerID string) { 102 // Get and remove listener. 103 evsw.mtx.RLock() 104 listener := evsw.listeners[listenerID] 105 evsw.mtx.RUnlock() 106 if listener == nil { 107 return 108 } 109 110 evsw.mtx.Lock() 111 delete(evsw.listeners, listenerID) 112 evsw.mtx.Unlock() 113 114 // Remove callback for each event. 115 listener.SetRemoved() 116 for _, event := range listener.GetEvents() { 117 evsw.RemoveListenerForEvent(event, listenerID) 118 } 119 } 120 121 func (evsw *eventSwitch) RemoveListenerForEvent(event string, listenerID string) { 122 // Get eventCell 123 evsw.mtx.Lock() 124 eventCell := evsw.eventCells[event] 125 evsw.mtx.Unlock() 126 127 if eventCell == nil { 128 return 129 } 130 131 // Remove listenerID from eventCell 132 numListeners := eventCell.RemoveListener(listenerID) 133 134 // Maybe garbage collect eventCell. 135 if numListeners == 0 { 136 // Lock again and double check. 137 evsw.mtx.Lock() // OUTER LOCK 138 eventCell.mtx.Lock() // INNER LOCK 139 if len(eventCell.listeners) == 0 { 140 delete(evsw.eventCells, event) 141 } 142 eventCell.mtx.Unlock() // INNER LOCK 143 evsw.mtx.Unlock() // OUTER LOCK 144 } 145 } 146 147 func (evsw *eventSwitch) FireEvent(event string, data EventData) { 148 // Get the eventCell 149 evsw.mtx.RLock() 150 eventCell := evsw.eventCells[event] 151 evsw.mtx.RUnlock() 152 153 if eventCell == nil { 154 return 155 } 156 157 // Fire event for all listeners in eventCell 158 eventCell.FireEvent(data) 159 } 160 161 //----------------------------------------------------------------------------- 162 163 // eventCell handles keeping track of listener callbacks for a given event. 164 type eventCell struct { 165 mtx tmsync.RWMutex 166 listeners map[string]EventCallback 167 } 168 169 func newEventCell() *eventCell { 170 return &eventCell{ 171 listeners: make(map[string]EventCallback), 172 } 173 } 174 175 func (cell *eventCell) AddListener(listenerID string, cb EventCallback) { 176 cell.mtx.Lock() 177 cell.listeners[listenerID] = cb 178 cell.mtx.Unlock() 179 } 180 181 func (cell *eventCell) RemoveListener(listenerID string) int { 182 cell.mtx.Lock() 183 delete(cell.listeners, listenerID) 184 numListeners := len(cell.listeners) 185 cell.mtx.Unlock() 186 return numListeners 187 } 188 189 func (cell *eventCell) FireEvent(data EventData) { 190 cell.mtx.RLock() 191 eventCallbacks := make([]EventCallback, 0, len(cell.listeners)) 192 for _, cb := range cell.listeners { 193 eventCallbacks = append(eventCallbacks, cb) 194 } 195 cell.mtx.RUnlock() 196 197 for _, cb := range eventCallbacks { 198 cb(data) 199 } 200 } 201 202 //----------------------------------------------------------------------------- 203 204 type EventCallback func(data EventData) 205 206 type eventListener struct { 207 id string 208 209 mtx tmsync.RWMutex 210 removed bool 211 events []string 212 } 213 214 func newEventListener(id string) *eventListener { 215 return &eventListener{ 216 id: id, 217 removed: false, 218 events: nil, 219 } 220 } 221 222 func (evl *eventListener) AddEvent(event string) error { 223 evl.mtx.Lock() 224 225 if evl.removed { 226 evl.mtx.Unlock() 227 return ErrListenerWasRemoved{listenerID: evl.id} 228 } 229 230 evl.events = append(evl.events, event) 231 evl.mtx.Unlock() 232 return nil 233 } 234 235 func (evl *eventListener) GetEvents() []string { 236 evl.mtx.RLock() 237 events := make([]string, len(evl.events)) 238 copy(events, evl.events) 239 evl.mtx.RUnlock() 240 return events 241 } 242 243 func (evl *eventListener) SetRemoved() { 244 evl.mtx.Lock() 245 evl.removed = true 246 evl.mtx.Unlock() 247 }