github.com/diamondburned/arikawa@v1.3.14/utils/handler/handler.go (about) 1 // Package handler handles incoming Gateway events. It reflects the function's 2 // first argument and caches that for use in each event. 3 // 4 // Performance 5 // 6 // Each call to the event would take 167 ns/op for roughly each handler. Scaling 7 // that up to 100 handlers is roughly the same as multiplying 167 ns by 100, 8 // which gives 16700 ns or 0.0167 ms. 9 // 10 // BenchmarkReflect-8 7260909 167 ns/op 11 // 12 // Usage 13 // 14 // Handler's usage is mostly similar to Discordgo, in that AddHandler expects a 15 // function with only one argument or an event channel. For more information, 16 // refer to AddHandler. 17 package handler 18 19 import ( 20 "context" 21 "fmt" 22 "reflect" 23 "sync" 24 25 "github.com/pkg/errors" 26 ) 27 28 type Handler struct { 29 // Synchronous controls whether to spawn each event handler in its own 30 // goroutine. Default false (meaning goroutines are spawned). 31 Synchronous bool 32 33 handlers map[uint64]handler 34 horders []uint64 35 hserial uint64 36 hmutex sync.RWMutex 37 } 38 39 func New() *Handler { 40 return &Handler{ 41 handlers: map[uint64]handler{}, 42 } 43 } 44 45 // Call calls all handlers with the given event. This is an internal method; use 46 // with care. 47 func (h *Handler) Call(ev interface{}) { 48 var evV = reflect.ValueOf(ev) 49 var evT = evV.Type() 50 51 h.hmutex.RLock() 52 defer h.hmutex.RUnlock() 53 54 for _, order := range h.horders { 55 handler, ok := h.handlers[order] 56 if !ok { 57 // This shouldn't ever happen, but we're adding this just in case. 58 continue 59 } 60 61 if handler.not(evT) { 62 continue 63 } 64 65 if h.Synchronous { 66 handler.call(evV) 67 } else { 68 go handler.call(evV) 69 } 70 } 71 } 72 73 // CallDirect is the same as Call, but only calls those event handlers that 74 // listen for this specific event, i.e. that aren't interface handlers. 75 func (h *Handler) CallDirect(ev interface{}) { 76 var evV = reflect.ValueOf(ev) 77 var evT = evV.Type() 78 79 h.hmutex.RLock() 80 defer h.hmutex.RUnlock() 81 82 for _, order := range h.horders { 83 handler, ok := h.handlers[order] 84 if !ok { 85 // This shouldn't ever happen, but we're adding this just in case. 86 continue 87 } 88 89 if evT != handler.event { 90 continue 91 } 92 93 if h.Synchronous { 94 handler.call(evV) 95 } else { 96 go handler.call(evV) 97 } 98 } 99 } 100 101 // WaitFor blocks until there's an event. It's advised to use ChanFor instead, 102 // as WaitFor may skip some events if it's not ran fast enough after the event 103 // arrived. 104 func (h *Handler) WaitFor(ctx context.Context, fn func(interface{}) bool) interface{} { 105 var result = make(chan interface{}) 106 107 cancel := h.AddHandler(func(v interface{}) { 108 if fn(v) { 109 result <- v 110 } 111 }) 112 defer cancel() 113 114 select { 115 case r := <-result: 116 return r 117 case <-ctx.Done(): 118 return nil 119 } 120 } 121 122 // ChanFor returns a channel that would receive all incoming events that match 123 // the callback given. The cancel() function removes the handler and drops all 124 // hanging goroutines. 125 // 126 // This method is more intended to be used as a filter. For a persistent event 127 // channel, consider adding it directly as a handler with AddHandler. 128 func (h *Handler) ChanFor(fn func(interface{}) bool) (out <-chan interface{}, cancel func()) { 129 result := make(chan interface{}) 130 closer := make(chan struct{}) 131 132 removeHandler := h.AddHandler(func(v interface{}) { 133 if fn(v) { 134 select { 135 case result <- v: 136 case <-closer: 137 } 138 } 139 }) 140 141 // Only allow cancel to be called once. 142 var once sync.Once 143 cancel = func() { 144 once.Do(func() { 145 removeHandler() 146 close(closer) 147 }) 148 } 149 out = result 150 151 return 152 } 153 154 // AddHandler adds the handler, returning a function that would remove this 155 // handler when called. A handler type is either a single-argument no-return 156 // function or a channel. 157 // 158 // Function 159 // 160 // A handler can be a function with a single argument that is the expected event 161 // type. It must not have any returns or any other number of arguments. 162 // 163 // // An example of a valid function handler. 164 // h.AddHandler(func(*gateway.MessageCreateEvent) {}) 165 // 166 // Channel 167 // 168 // A handler can also be a channel. The underlying type that the channel wraps 169 // around will be the event type. As such, the type rules are the same as 170 // function handlers. 171 // 172 // Keep in mind that the user must NOT close the channel. In fact, the channel 173 // should not be closed at all. The caller function WILL PANIC if the channel is 174 // closed! 175 // 176 // When the rm callback that is returned is called, it will also guarantee that 177 // all blocking sends will be cancelled. This helps prevent dangling goroutines. 178 // 179 // // An example of a valid channel handler. 180 // ch := make(chan *gateway.MessageCreateEvent) 181 // h.AddHandler(ch) 182 // 183 func (h *Handler) AddHandler(handler interface{}) (rm func()) { 184 rm, err := h.addHandler(handler) 185 if err != nil { 186 panic(err) 187 } 188 return rm 189 } 190 191 // AddHandlerCheck adds the handler, but safe-guards reflect panics with a 192 // recoverer, returning the error. Refer to AddHandler for more information. 193 func (h *Handler) AddHandlerCheck(handler interface{}) (rm func(), err error) { 194 // Reflect would actually panic if anything goes wrong, so this is just in 195 // case. 196 defer func() { 197 if rec := recover(); rec != nil { 198 if recErr, ok := rec.(error); ok { 199 err = recErr 200 } else { 201 err = fmt.Errorf("%v", rec) 202 } 203 } 204 }() 205 206 return h.addHandler(handler) 207 } 208 209 func (h *Handler) addHandler(fn interface{}) (rm func(), err error) { 210 // Reflect the handler 211 r, err := newHandler(fn) 212 if err != nil { 213 return nil, errors.Wrap(err, "handler reflect failed") 214 } 215 216 h.hmutex.Lock() 217 defer h.hmutex.Unlock() 218 219 // Get the current counter value and increment the counter: 220 serial := h.hserial 221 h.hserial++ 222 223 // Create a map if there's none: 224 if h.handlers == nil { 225 h.handlers = map[uint64]handler{} 226 } 227 228 // Use the serial for the map: 229 h.handlers[serial] = *r 230 231 // Append the serial into the list of keys: 232 h.horders = append(h.horders, serial) 233 234 return func() { 235 h.hmutex.Lock() 236 defer h.hmutex.Unlock() 237 238 // Take and delete the handler from the map, but return if we can't find 239 // it. 240 hd, ok := h.handlers[serial] 241 if !ok { 242 return 243 } 244 245 delete(h.handlers, serial) 246 247 // Delete the key from the orders slice: 248 for i, order := range h.horders { 249 if order == serial { 250 h.horders = append(h.horders[:i], h.horders[i+1:]...) 251 break 252 } 253 } 254 255 // Clean up the handler. 256 hd.cleanup() 257 }, nil 258 } 259 260 type handler struct { 261 event reflect.Type // underlying type; arg0 or chan underlying type 262 callback reflect.Value 263 isIface bool 264 chanclose reflect.Value // IsValid() if chan 265 } 266 267 // newHandler reflects either a channel or a function into a handler. A function 268 // must only have a single argument being the event and no return, and a channel 269 // must have the event type as the underlying type. 270 func newHandler(unknown interface{}) (*handler, error) { 271 fnV := reflect.ValueOf(unknown) 272 fnT := fnV.Type() 273 274 // underlying event type 275 var handler = handler{ 276 callback: fnV, 277 } 278 279 switch fnT.Kind() { 280 case reflect.Func: 281 if fnT.NumIn() != 1 { 282 return nil, errors.New("function can only accept 1 event as argument") 283 } 284 285 if fnT.NumOut() > 0 { 286 return nil, errors.New("function can't accept returns") 287 } 288 289 handler.event = fnT.In(0) 290 291 case reflect.Chan: 292 handler.event = fnT.Elem() 293 handler.chanclose = reflect.ValueOf(make(chan struct{})) 294 295 default: 296 return nil, errors.New("given interface is not a function or channel") 297 } 298 299 var kind = handler.event.Kind() 300 301 // Accept either pointer type or interface{} type 302 if kind != reflect.Ptr && kind != reflect.Interface { 303 return nil, errors.New("first argument is not pointer") 304 } 305 306 handler.isIface = kind == reflect.Interface 307 308 return &handler, nil 309 } 310 311 func (h handler) not(event reflect.Type) bool { 312 if h.isIface { 313 return !event.Implements(h.event) 314 } 315 316 return h.event != event 317 } 318 319 func (h *handler) call(event reflect.Value) { 320 if h.chanclose.IsValid() { 321 reflect.Select([]reflect.SelectCase{ 322 {Dir: reflect.SelectSend, Chan: h.callback, Send: event}, 323 {Dir: reflect.SelectRecv, Chan: h.chanclose}, 324 }) 325 } else { 326 h.callback.Call([]reflect.Value{event}) 327 } 328 } 329 330 func (h *handler) cleanup() { 331 if h.chanclose.IsValid() { 332 // Closing this channel will force all ongoing selects to return 333 // immediately. 334 h.chanclose.Close() 335 } 336 }