github.com/chain5j/chain5j-pkg@v1.0.7/eventbus/event_bus.go (about) 1 package EventBus 2 3 import ( 4 "fmt" 5 "reflect" 6 "runtime/debug" 7 "sync" 8 9 "github.com/chain5j/logger" 10 ) 11 12 // BusSubscriber defines subscription-related bus behavior 13 type BusSubscriber interface { 14 Subscribe(topic string, fn interface{}) error 15 SubscribeAsync(topic string, fn interface{}, transactional bool) error 16 SubscribeOnce(topic string, fn interface{}) error 17 SubscribeOnceAsync(topic string, fn interface{}) error 18 Unsubscribe(topic string, handler interface{}) error 19 } 20 21 // BusPublisher defines publishing-related bus behavior 22 type BusPublisher interface { 23 Publish(topic string, args ...interface{}) 24 } 25 26 // BusController defines bus control behavior (checking handler's presence, synchronization) 27 type BusController interface { 28 HasCallback(topic string) bool 29 WaitAsync() 30 } 31 32 // Bus englobes global (subscribe, publish, control) bus behavior 33 type Bus interface { 34 BusController 35 BusSubscriber 36 BusPublisher 37 } 38 39 // EventBus - box for handlers and callbacks. 40 type EventBus struct { 41 // handlers map[string][]*eventHandler 42 handlers sync.Map 43 wg sync.WaitGroup 44 } 45 46 type eventHandler struct { 47 callBack reflect.Value 48 flagOnce bool 49 async bool 50 transactional bool 51 sync.Mutex // lock for an event handler - useful for running async callbacks serially 52 } 53 54 // New returns new EventBus with empty handlers. 55 func New() Bus { 56 b := &EventBus{ 57 sync.Map{}, 58 sync.WaitGroup{}, 59 } 60 return Bus(b) 61 } 62 63 // doSubscribe handles the subscription logic and is utilized by the public Subscribe functions 64 func (bus *EventBus) doSubscribe(topic string, fn interface{}, handler *eventHandler) error { 65 if !(reflect.TypeOf(fn).Kind() == reflect.Func) { 66 return fmt.Errorf("%s is not of type reflect.Func", reflect.TypeOf(fn).Kind()) 67 } 68 if value, ok := bus.handlers.Load(topic); ok { 69 handlers := value.([]*eventHandler) 70 handlers = append(handlers, handler) 71 bus.handlers.Store(topic, handlers) 72 } else { 73 handlers := make([]*eventHandler, 0) 74 handlers = append(handlers, handler) 75 bus.handlers.Store(topic, handlers) 76 } 77 return nil 78 } 79 80 // Subscribe subscribes to a topic. 81 // Returns error if `fn` is not a function. 82 func (bus *EventBus) Subscribe(topic string, fn interface{}) error { 83 return bus.doSubscribe(topic, fn, &eventHandler{ 84 reflect.ValueOf(fn), false, false, false, sync.Mutex{}, 85 }) 86 } 87 88 // SubscribeAsync subscribes to a topic with an asynchronous callback 89 // Transactional determines whether subsequent callbacks for a topic are 90 // run serially (true) or concurrently (false) 91 // Returns error if `fn` is not a function. 92 func (bus *EventBus) SubscribeAsync(topic string, fn interface{}, transactional bool) error { 93 return bus.doSubscribe(topic, fn, &eventHandler{ 94 reflect.ValueOf(fn), false, true, transactional, sync.Mutex{}, 95 }) 96 } 97 98 // SubscribeOnce subscribes to a topic once. Handler will be removed after executing. 99 // Returns error if `fn` is not a function. 100 func (bus *EventBus) SubscribeOnce(topic string, fn interface{}) error { 101 return bus.doSubscribe(topic, fn, &eventHandler{ 102 reflect.ValueOf(fn), true, false, false, sync.Mutex{}, 103 }) 104 } 105 106 // SubscribeOnceAsync subscribes to a topic once with an asynchronous callback 107 // Handler will be removed after executing. 108 // Returns error if `fn` is not a function. 109 func (bus *EventBus) SubscribeOnceAsync(topic string, fn interface{}) error { 110 return bus.doSubscribe(topic, fn, &eventHandler{ 111 reflect.ValueOf(fn), true, true, false, sync.Mutex{}, 112 }) 113 } 114 115 // HasCallback returns true if exists any callback subscribed to the topic. 116 func (bus *EventBus) HasCallback(topic string) bool { 117 if val, ok := bus.handlers.Load(topic); ok { 118 return len(val.([]*eventHandler)) > 0 119 } 120 return false 121 } 122 123 // Unsubscribe removes callback defined for a topic. 124 // Returns error if there are no callbacks subscribed to the topic. 125 func (bus *EventBus) Unsubscribe(topic string, handler interface{}) error { 126 bus.HasCallback(topic) 127 if bus.HasCallback(topic) { 128 bus.removeHandler(topic, bus.findHandlerIdx(topic, reflect.ValueOf(handler))) 129 return nil 130 } 131 return fmt.Errorf("topic %s doesn't exist", topic) 132 } 133 134 // Publish executes callback defined for a topic. Any additional argument will be transferred to the callback. 135 func (bus *EventBus) Publish(topic string, args ...interface{}) { 136 if val, ok := bus.handlers.Load(topic); ok { 137 handlers := val.([]*eventHandler) 138 if 0 < len(handlers) { 139 // Handlers slice may be changed by removeHandler and Unsubscribe during iteration, 140 // so make a copy and iterate the copied slice. 141 copyHandlers := make([]*eventHandler, len(handlers)) 142 copy(copyHandlers, handlers) 143 for i, handler := range copyHandlers { 144 if handler.flagOnce { 145 bus.removeHandler(topic, i) 146 } 147 if !handler.async { 148 bus.doPublish(handler, topic, args...) 149 } else { 150 bus.wg.Add(1) 151 if handler.transactional { 152 handler.Lock() 153 } 154 go bus.doPublishAsync(handler, topic, args...) 155 } 156 } 157 } 158 } 159 } 160 161 func (bus *EventBus) doPublish(handler *eventHandler, topic string, args ...interface{}) { 162 defer func() { 163 if err := recover(); err != nil { 164 logger.Error("event bus doPublish recover", "topic", topic, "err", err) 165 debug.PrintStack() 166 } 167 }() 168 passedArguments := bus.setUpPublish(handler, args...) 169 handler.callBack.Call(passedArguments) 170 } 171 172 func (bus *EventBus) doPublishAsync(handler *eventHandler, topic string, args ...interface{}) { 173 defer bus.wg.Done() 174 if handler.transactional { 175 defer handler.Unlock() 176 } 177 bus.doPublish(handler, topic, args...) 178 } 179 180 func (bus *EventBus) removeHandler(topic string, idx int) { 181 val, ok := bus.handlers.Load(topic) 182 if !ok { 183 return 184 } 185 handlers := val.([]*eventHandler) 186 l := len(handlers) 187 188 if !(0 <= idx && idx < l) { 189 return 190 } 191 192 copy(handlers[idx:], handlers[idx+1:]) 193 handlers[l-1] = nil // or the zero value of T 194 bus.handlers.Store(topic, handlers[:l-1]) 195 } 196 197 func (bus *EventBus) findHandlerIdx(topic string, callback reflect.Value) int { 198 if val, ok := bus.handlers.Load(topic); ok { 199 handlers := val.([]*eventHandler) 200 for idx, handler := range handlers { 201 if handler.callBack.Type() == callback.Type() && 202 handler.callBack.Pointer() == callback.Pointer() { 203 return idx 204 } 205 } 206 } 207 return -1 208 } 209 210 func (bus *EventBus) setUpPublish(callback *eventHandler, args ...interface{}) []reflect.Value { 211 funcType := callback.callBack.Type() 212 passedArguments := make([]reflect.Value, len(args)) 213 for i, v := range args { 214 if v == nil { 215 passedArguments[i] = reflect.New(funcType.In(i)).Elem() 216 } else { 217 passedArguments[i] = reflect.ValueOf(v) 218 } 219 } 220 221 return passedArguments 222 } 223 224 // WaitAsync waits for all async callbacks to complete 225 func (bus *EventBus) WaitAsync() { 226 bus.wg.Wait() 227 }