goirc/event/registry.go

164 lines
3.2 KiB
Go
Raw Normal View History

package event
2011-07-27 15:54:49 +00:00
import (
"container/list"
"strings"
"sync"
2011-11-13 13:32:10 +00:00
"sync/atomic"
2011-07-27 15:54:49 +00:00
)
type HandlerID uint32
2011-07-27 15:54:49 +00:00
var hidCounter uint32 = 0
func NewHandlerID() HandlerID {
return HandlerID(atomic.AddUint32(&hidCounter, 1))
}
type Handler interface {
Run(...interface{})
Id() HandlerID
}
type basicHandler struct {
fn func(...interface{})
id HandlerID
}
func (h *basicHandler) Run(ev ...interface{}) {
h.fn(ev...)
}
func (h *basicHandler) Id() HandlerID {
return h.id
}
func NewHandler(h func(...interface{})) Handler {
return &basicHandler{h, NewHandlerID()}
2011-07-27 15:54:49 +00:00
}
type EventDispatcher interface {
Dispatch(name string, ev ...interface{})
}
2011-07-27 15:54:49 +00:00
type EventRegistry interface {
AddHandler(h Handler, names ...string)
DelHandler(h Handler, names ...string)
2011-07-27 15:54:49 +00:00
Dispatch(name string, ev ...interface{})
ClearEvents(name string)
}
type registry struct {
// Event registry as a lockable map of linked-lists
sync.RWMutex
events map[string]*list.List
dispatcher func(r *registry, name string, ev ...interface{})
}
2011-07-27 15:54:49 +00:00
func NewRegistry() *registry {
2011-07-27 15:54:49 +00:00
r := &registry{events: make(map[string]*list.List)}
r.Parallel()
return r
}
func (r *registry) AddHandler(h Handler, names ...string) {
if len(names) == 0 {
return
}
2011-07-27 15:54:49 +00:00
r.Lock()
defer r.Unlock()
2011-11-13 13:32:10 +00:00
N:
for _, name := range names {
name = strings.ToLower(name)
if _, ok := r.events[name]; !ok {
r.events[name] = list.New()
}
for e := r.events[name].Front(); e != nil; e = e.Next() {
// Check we're not adding a duplicate handler to this event
if e.Value.(Handler).Id() == h.Id() {
continue N
}
}
r.events[name].PushBack(h)
2011-07-27 15:54:49 +00:00
}
}
func _del(l *list.List, id HandlerID) bool {
for e := l.Front(); e != nil; e = e.Next() {
if e.Value.(Handler).Id() == id {
l.Remove(e)
2011-07-27 15:54:49 +00:00
}
}
return l.Len() == 0
2011-07-27 15:54:49 +00:00
}
func (r *registry) DelHandler(h Handler, names ...string) {
2011-07-27 15:54:49 +00:00
r.Lock()
defer r.Unlock()
if len(names) == 0 {
for name, l := range r.events {
if _del(l, h.Id()) {
2011-11-13 13:32:10 +00:00
delete(r.events, name)
}
}
} else {
for _, name := range names {
name = strings.ToLower(name)
if l, ok := r.events[name]; ok {
if _del(l, h.Id()) {
2011-11-13 13:32:10 +00:00
delete(r.events, name)
}
2011-07-27 15:54:49 +00:00
}
}
}
}
func (r *registry) Dispatch(name string, ev ...interface{}) {
r.dispatcher(r, strings.ToLower(name), ev...)
2011-07-27 15:54:49 +00:00
}
func (r *registry) ClearEvents(name string) {
name = strings.ToLower(name)
r.Lock()
defer r.Unlock()
if l, ok := r.events[name]; ok {
l.Init() // I hope this is enough to GC all list elements.
2011-11-13 13:32:10 +00:00
delete(r.events, name)
}
}
2011-07-27 15:54:49 +00:00
func (r *registry) Parallel() {
r.dispatcher = (*registry).parallelDispatch
2011-07-27 15:54:49 +00:00
}
func (r *registry) Serial() {
r.dispatcher = (*registry).serialDispatch
2011-07-27 15:54:49 +00:00
}
func (r *registry) parallelDispatch(name string, ev ...interface{}) {
r.RLock()
defer r.RUnlock()
if l, ok := r.events[name]; ok {
for e := l.Front(); e != nil; e = e.Next() {
h := e.Value.(Handler)
go h.Run(ev...)
2011-07-27 15:54:49 +00:00
}
}
}
func (r *registry) serialDispatch(name string, ev ...interface{}) {
r.RLock()
defer r.RUnlock()
if l, ok := r.events[name]; ok {
hlist := make([]Handler, l.Len())
for e, i := l.Front(), 0; e != nil; e, i = e.Next(), i+1 {
hlist[i] = e.Value.(Handler)
2011-07-27 15:54:49 +00:00
}
go func() {
for _, h := range hlist {
h.Run(ev...)
2011-07-27 15:54:49 +00:00
}
}()
}
}