github.com/diamondburned/arikawa/v2@v2.1.0/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 // Handler is a container for command handlers. A zero-value instance is a valid 29 // instance. 30 type Handler struct { 31 // Synchronous controls whether to spawn each event handler in its own 32 // goroutine. Default false (meaning goroutines are spawned). 33 Synchronous bool 34 35 mutex sync.RWMutex 36 slab slab 37 } 38 39 func New() *Handler { 40 return &Handler{} 41 } 42 43 // Call calls all handlers with the given event. This is an internal method; use 44 // with care. 45 func (h *Handler) Call(ev interface{}) { 46 var evV = reflect.ValueOf(ev) 47 var evT = evV.Type() 48 49 h.mutex.RLock() 50 defer h.mutex.RUnlock() 51 52 for _, entry := range h.slab.Entries { 53 if entry.isInvalid() || entry.not(evT) { 54 continue 55 } 56 57 if h.Synchronous { 58 entry.call(evV) 59 } else { 60 go entry.call(evV) 61 } 62 } 63 } 64 65 // WaitFor blocks until there's an event. It's advised to use ChanFor instead, 66 // as WaitFor may skip some events if it's not ran fast enough after the event 67 // arrived. 68 func (h *Handler) WaitFor(ctx context.Context, fn func(interface{}) bool) interface{} { 69 var result = make(chan interface{}) 70 71 cancel := h.AddHandler(func(v interface{}) { 72 if fn(v) { 73 result <- v 74 } 75 }) 76 defer cancel() 77 78 select { 79 case r := <-result: 80 return r 81 case <-ctx.Done(): 82 return nil 83 } 84 } 85 86 // ChanFor returns a channel that would receive all incoming events that match 87 // the callback given. The cancel() function removes the handler and drops all 88 // hanging goroutines. 89 // 90 // This method is more intended to be used as a filter. For a persistent event 91 // channel, consider adding it directly as a handler with AddHandler. 92 func (h *Handler) ChanFor(fn func(interface{}) bool) (out <-chan interface{}, cancel func()) { 93 result := make(chan interface{}) 94 closer := make(chan struct{}) 95 96 removeHandler := h.AddHandler(func(v interface{}) { 97 if fn(v) { 98 select { 99 case result <- v: 100 case <-closer: 101 } 102 } 103 }) 104 105 // Only allow cancel to be called once. 106 var once sync.Once 107 cancel = func() { 108 once.Do(func() { 109 removeHandler() 110 close(closer) 111 }) 112 } 113 out = result 114 115 return 116 } 117 118 // AddHandler adds the handler, returning a function that would remove this 119 // handler when called. A handler type is either a single-argument no-return 120 // function or a channel. 121 // 122 // Function 123 // 124 // A handler can be a function with a single argument that is the expected event 125 // type. It must not have any returns or any other number of arguments. 126 // 127 // // An example of a valid function handler. 128 // h.AddHandler(func(*gateway.MessageCreateEvent) {}) 129 // 130 // Channel 131 // 132 // A handler can also be a channel. The underlying type that the channel wraps 133 // around will be the event type. As such, the type rules are the same as 134 // function handlers. 135 // 136 // Keep in mind that the user must NOT close the channel. In fact, the channel 137 // should not be closed at all. The caller function WILL PANIC if the channel is 138 // closed! 139 // 140 // When the rm callback that is returned is called, it will also guarantee that 141 // all blocking sends will be cancelled. This helps prevent dangling goroutines. 142 // 143 // // An example of a valid channel handler. 144 // ch := make(chan *gateway.MessageCreateEvent) 145 // h.AddHandler(ch) 146 // 147 func (h *Handler) AddHandler(handler interface{}) (rm func()) { 148 rm, err := h.addHandler(handler) 149 if err != nil { 150 panic(err) 151 } 152 return rm 153 } 154 155 // AddHandlerCheck adds the handler, but safe-guards reflect panics with a 156 // recoverer, returning the error. Refer to AddHandler for more information. 157 func (h *Handler) AddHandlerCheck(handler interface{}) (rm func(), err error) { 158 // Reflect would actually panic if anything goes wrong, so this is just in 159 // case. 160 defer func() { 161 if rec := recover(); rec != nil { 162 if recErr, ok := rec.(error); ok { 163 err = recErr 164 } else { 165 err = fmt.Errorf("%v", rec) 166 } 167 } 168 }() 169 170 return h.addHandler(handler) 171 } 172 173 func (h *Handler) addHandler(fn interface{}) (rm func(), err error) { 174 // Reflect the handler 175 r, err := newHandler(fn) 176 if err != nil { 177 return nil, errors.Wrap(err, "handler reflect failed") 178 } 179 180 h.mutex.Lock() 181 id := h.slab.Put(r) 182 h.mutex.Unlock() 183 184 return func() { 185 h.mutex.Lock() 186 popped := h.slab.Pop(id) 187 h.mutex.Unlock() 188 189 popped.cleanup() 190 }, nil 191 } 192 193 type handler struct { 194 event reflect.Type // underlying type; arg0 or chan underlying type 195 callback reflect.Value 196 isIface bool 197 chanclose reflect.Value // IsValid() if chan 198 } 199 200 // newHandler reflects either a channel or a function into a handler. A function 201 // must only have a single argument being the event and no return, and a channel 202 // must have the event type as the underlying type. 203 func newHandler(unknown interface{}) (handler, error) { 204 fnV := reflect.ValueOf(unknown) 205 fnT := fnV.Type() 206 207 // underlying event type 208 var handler = handler{ 209 callback: fnV, 210 } 211 212 switch fnT.Kind() { 213 case reflect.Func: 214 if fnT.NumIn() != 1 { 215 return handler, errors.New("function can only accept 1 event as argument") 216 } 217 218 if fnT.NumOut() > 0 { 219 return handler, errors.New("function can't accept returns") 220 } 221 222 handler.event = fnT.In(0) 223 224 case reflect.Chan: 225 handler.event = fnT.Elem() 226 handler.chanclose = reflect.ValueOf(make(chan struct{})) 227 228 default: 229 return handler, errors.New("given interface is not a function or channel") 230 } 231 232 var kind = handler.event.Kind() 233 234 // Accept either pointer type or interface{} type 235 if kind != reflect.Ptr && kind != reflect.Interface { 236 return handler, errors.New("first argument is not pointer") 237 } 238 239 handler.isIface = kind == reflect.Interface 240 241 return handler, nil 242 } 243 244 func (h handler) not(event reflect.Type) bool { 245 if h.isIface { 246 return !event.Implements(h.event) 247 } 248 249 return h.event != event 250 } 251 252 func (h handler) call(event reflect.Value) { 253 if h.chanclose.IsValid() { 254 reflect.Select([]reflect.SelectCase{ 255 {Dir: reflect.SelectSend, Chan: h.callback, Send: event}, 256 {Dir: reflect.SelectRecv, Chan: h.chanclose}, 257 }) 258 } else { 259 h.callback.Call([]reflect.Value{event}) 260 } 261 } 262 263 func (h handler) cleanup() { 264 if h.chanclose.IsValid() { 265 // Closing this channel will force all ongoing selects to return 266 // immediately. 267 h.chanclose.Close() 268 } 269 }