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  }