mirror of
https://github.com/diamondburned/arikawa.git
synced 2024-11-20 05:43:21 +00:00
Handler: Changed to a linked list instead of a slice-backed map
This change should slightly improve the performance of the handler container. A rough benchmark was written and tested; the source code is at https://gist.github.com/diamondburned/c369d13efda5c702a0e59874deee64bd.
This commit is contained in:
parent
b7b8118d0b
commit
ef48d686cd
|
@ -17,6 +17,7 @@
|
||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/list"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -25,21 +26,19 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Handler is a container for command handlers. A zero-value instance is a valid
|
||||||
|
// instance.
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
// Synchronous controls whether to spawn each event handler in its own
|
// Synchronous controls whether to spawn each event handler in its own
|
||||||
// goroutine. Default false (meaning goroutines are spawned).
|
// goroutine. Default false (meaning goroutines are spawned).
|
||||||
Synchronous bool
|
Synchronous bool
|
||||||
|
|
||||||
handlers map[uint64]handler
|
mutex sync.RWMutex
|
||||||
horders []uint64
|
list list.List
|
||||||
hserial uint64
|
|
||||||
hmutex sync.RWMutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Handler {
|
func New() *Handler {
|
||||||
return &Handler{
|
return &Handler{}
|
||||||
handlers: map[uint64]handler{},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call calls all handlers with the given event. This is an internal method; use
|
// Call calls all handlers with the given event. This is an internal method; use
|
||||||
|
@ -48,15 +47,11 @@ func (h *Handler) Call(ev interface{}) {
|
||||||
var evV = reflect.ValueOf(ev)
|
var evV = reflect.ValueOf(ev)
|
||||||
var evT = evV.Type()
|
var evT = evV.Type()
|
||||||
|
|
||||||
h.hmutex.RLock()
|
h.mutex.RLock()
|
||||||
defer h.hmutex.RUnlock()
|
defer h.mutex.RUnlock()
|
||||||
|
|
||||||
for _, order := range h.horders {
|
for elem := h.list.Front(); elem != nil; elem = elem.Next() {
|
||||||
handler, ok := h.handlers[order]
|
handler := elem.Value.(handler)
|
||||||
if !ok {
|
|
||||||
// This shouldn't ever happen, but we're adding this just in case.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if handler.not(evT) {
|
if handler.not(evT) {
|
||||||
continue
|
continue
|
||||||
|
@ -70,34 +65,6 @@ func (h *Handler) Call(ev interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallDirect is the same as Call, but only calls those event handlers that
|
|
||||||
// listen for this specific event, i.e. that aren't interface handlers.
|
|
||||||
func (h *Handler) CallDirect(ev interface{}) {
|
|
||||||
var evV = reflect.ValueOf(ev)
|
|
||||||
var evT = evV.Type()
|
|
||||||
|
|
||||||
h.hmutex.RLock()
|
|
||||||
defer h.hmutex.RUnlock()
|
|
||||||
|
|
||||||
for _, order := range h.horders {
|
|
||||||
handler, ok := h.handlers[order]
|
|
||||||
if !ok {
|
|
||||||
// This shouldn't ever happen, but we're adding this just in case.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if evT != handler.event {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if h.Synchronous {
|
|
||||||
handler.call(evV)
|
|
||||||
} else {
|
|
||||||
go handler.call(evV)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WaitFor blocks until there's an event. It's advised to use ChanFor instead,
|
// WaitFor blocks until there's an event. It's advised to use ChanFor instead,
|
||||||
// as WaitFor may skip some events if it's not ran fast enough after the event
|
// as WaitFor may skip some events if it's not ran fast enough after the event
|
||||||
// arrived.
|
// arrived.
|
||||||
|
@ -213,47 +180,18 @@ func (h *Handler) addHandler(fn interface{}) (rm func(), err error) {
|
||||||
return nil, errors.Wrap(err, "handler reflect failed")
|
return nil, errors.Wrap(err, "handler reflect failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
h.hmutex.Lock()
|
h.mutex.RLock()
|
||||||
defer h.hmutex.Unlock()
|
elem := h.list.PushBack(*r)
|
||||||
|
h.mutex.RUnlock()
|
||||||
// Get the current counter value and increment the counter:
|
|
||||||
serial := h.hserial
|
|
||||||
h.hserial++
|
|
||||||
|
|
||||||
// Create a map if there's none:
|
|
||||||
if h.handlers == nil {
|
|
||||||
h.handlers = map[uint64]handler{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the serial for the map:
|
|
||||||
h.handlers[serial] = *r
|
|
||||||
|
|
||||||
// Append the serial into the list of keys:
|
|
||||||
h.horders = append(h.horders, serial)
|
|
||||||
|
|
||||||
return func() {
|
return func() {
|
||||||
h.hmutex.Lock()
|
h.mutex.Lock()
|
||||||
defer h.hmutex.Unlock()
|
v := h.list.Remove(elem)
|
||||||
|
h.mutex.Unlock()
|
||||||
// Take and delete the handler from the map, but return if we can't find
|
|
||||||
// it.
|
|
||||||
hd, ok := h.handlers[serial]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(h.handlers, serial)
|
|
||||||
|
|
||||||
// Delete the key from the orders slice:
|
|
||||||
for i, order := range h.horders {
|
|
||||||
if order == serial {
|
|
||||||
h.horders = append(h.horders[:i], h.horders[i+1:]...)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up the handler.
|
// Clean up the handler.
|
||||||
hd.cleanup()
|
handler := v.(handler)
|
||||||
|
handler.cleanup()
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue