bitbucket.org/number571/tendermint@v0.8.14/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 tmsync "bitbucket.org/number571/tendermint/internal/libs/sync" 8 "bitbucket.org/number571/tendermint/libs/service" 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(eventValue 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, eventValue 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, eventValue string, cb EventCallback) error { 78 // Get/Create eventCell and listener. 79 evsw.mtx.Lock() 80 81 eventCell := evsw.eventCells[eventValue] 82 if eventCell == nil { 83 eventCell = newEventCell() 84 evsw.eventCells[eventValue] = eventCell 85 } 86 87 listener := evsw.listeners[listenerID] 88 if listener == nil { 89 listener = newEventListener(listenerID) 90 evsw.listeners[listenerID] = listener 91 } 92 93 evsw.mtx.Unlock() 94 95 if err := listener.AddEvent(eventValue); err != nil { 96 return err 97 } 98 99 eventCell.AddListener(listenerID, cb) 100 return nil 101 } 102 103 func (evsw *eventSwitch) RemoveListener(listenerID string) { 104 // Get and remove listener. 105 evsw.mtx.RLock() 106 listener := evsw.listeners[listenerID] 107 evsw.mtx.RUnlock() 108 if listener == nil { 109 return 110 } 111 112 evsw.mtx.Lock() 113 delete(evsw.listeners, listenerID) 114 evsw.mtx.Unlock() 115 116 // Remove callback for each event. 117 listener.SetRemoved() 118 for _, event := range listener.GetEvents() { 119 evsw.RemoveListenerForEvent(event, listenerID) 120 } 121 } 122 123 func (evsw *eventSwitch) RemoveListenerForEvent(event string, listenerID string) { 124 // Get eventCell 125 evsw.mtx.Lock() 126 eventCell := evsw.eventCells[event] 127 evsw.mtx.Unlock() 128 129 if eventCell == nil { 130 return 131 } 132 133 // Remove listenerID from eventCell 134 numListeners := eventCell.RemoveListener(listenerID) 135 136 // Maybe garbage collect eventCell. 137 if numListeners == 0 { 138 // Lock again and double check. 139 evsw.mtx.Lock() // OUTER LOCK 140 eventCell.mtx.Lock() // INNER LOCK 141 if len(eventCell.listeners) == 0 { 142 delete(evsw.eventCells, event) 143 } 144 eventCell.mtx.Unlock() // INNER LOCK 145 evsw.mtx.Unlock() // OUTER LOCK 146 } 147 } 148 149 func (evsw *eventSwitch) FireEvent(event string, data EventData) { 150 // Get the eventCell 151 evsw.mtx.RLock() 152 eventCell := evsw.eventCells[event] 153 evsw.mtx.RUnlock() 154 155 if eventCell == nil { 156 return 157 } 158 159 // Fire event for all listeners in eventCell 160 eventCell.FireEvent(data) 161 } 162 163 //----------------------------------------------------------------------------- 164 165 // eventCell handles keeping track of listener callbacks for a given event. 166 type eventCell struct { 167 mtx tmsync.RWMutex 168 listeners map[string]EventCallback 169 } 170 171 func newEventCell() *eventCell { 172 return &eventCell{ 173 listeners: make(map[string]EventCallback), 174 } 175 } 176 177 func (cell *eventCell) AddListener(listenerID string, cb EventCallback) { 178 cell.mtx.Lock() 179 cell.listeners[listenerID] = cb 180 cell.mtx.Unlock() 181 } 182 183 func (cell *eventCell) RemoveListener(listenerID string) int { 184 cell.mtx.Lock() 185 delete(cell.listeners, listenerID) 186 numListeners := len(cell.listeners) 187 cell.mtx.Unlock() 188 return numListeners 189 } 190 191 func (cell *eventCell) FireEvent(data EventData) { 192 cell.mtx.RLock() 193 eventCallbacks := make([]EventCallback, 0, len(cell.listeners)) 194 for _, cb := range cell.listeners { 195 eventCallbacks = append(eventCallbacks, cb) 196 } 197 cell.mtx.RUnlock() 198 199 for _, cb := range eventCallbacks { 200 cb(data) 201 } 202 } 203 204 //----------------------------------------------------------------------------- 205 206 type EventCallback func(data EventData) 207 208 type eventListener struct { 209 id string 210 211 mtx tmsync.RWMutex 212 removed bool 213 events []string 214 } 215 216 func newEventListener(id string) *eventListener { 217 return &eventListener{ 218 id: id, 219 removed: false, 220 events: nil, 221 } 222 } 223 224 func (evl *eventListener) AddEvent(event string) error { 225 evl.mtx.Lock() 226 227 if evl.removed { 228 evl.mtx.Unlock() 229 return ErrListenerWasRemoved{listenerID: evl.id} 230 } 231 232 evl.events = append(evl.events, event) 233 evl.mtx.Unlock() 234 return nil 235 } 236 237 func (evl *eventListener) GetEvents() []string { 238 evl.mtx.RLock() 239 events := make([]string, len(evl.events)) 240 copy(events, evl.events) 241 evl.mtx.RUnlock() 242 return events 243 } 244 245 func (evl *eventListener) SetRemoved() { 246 evl.mtx.Lock() 247 evl.removed = true 248 evl.mtx.Unlock() 249 }