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  }