Last active
January 1, 2016 16:17
-
-
Save maciekmm/4c291b6c8c0ac789efba to your computer and use it in GitHub Desktop.
Event dispatcher
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package events | |
import ( | |
"fmt" | |
"reflect" | |
"sync" | |
) | |
type Event interface { | |
Clone() Event | |
} | |
type Dispatcher struct { | |
handlers map[reflect.Type][]reflect.Value | |
lock *sync.RWMutex | |
} | |
func NewDispatcher() *Dispatcher { | |
return &Dispatcher{ | |
handlers: make(map[reflect.Type][]reflect.Value), | |
lock: &sync.RWMutex{}, | |
} | |
} | |
// RegisterEvent registers custom events making it possible to register listeners for them. | |
// It has to be mentioned that in order to listen for the event it has to be registered firstly. | |
// It is REQUIRED to override Clone() method provided by anonymous struct BaseEvent, | |
// otherwise thesame copy will be sent to all listeners | |
func (d *Dispatcher) RegisterEvent(event Event) bool { | |
d.lock.Lock() | |
defer d.lock.Unlock() | |
typ := reflect.TypeOf(event).Elem() | |
fmt.Println(typ) | |
if _, ok := d.handlers[typ]; ok { | |
return false | |
} | |
var chanArr []reflect.Value | |
d.handlers[typ] = chanArr | |
return true | |
} | |
// RegisterListener registers chanel accepting desired event - a listener. | |
// It is important to note that what channel accepts determines what will be sent to it. | |
// If listened event is not registered false is returned | |
// It's advised to use a buffered channel for the listener, otherwise events might be lost in action. | |
// Panics if pipe is not a channel | |
func (d *Dispatcher) RegisterListener(pipe interface{}) bool { | |
d.lock.Lock() | |
defer d.lock.Unlock() | |
channelValue := reflect.ValueOf(pipe) | |
channelType := channelValue.Type() | |
if channelType.Kind() != reflect.Chan { | |
panic("Trying to register a non-channel listener") | |
} | |
channelIn := channelType.Elem() | |
if arr, ok := d.handlers[channelIn]; ok { | |
d.handlers[channelIn] = append(arr, channelValue) | |
return true | |
} | |
return false | |
} | |
// Dispatch provides thread safe method to send event to all listeners | |
// Returns true if succeded and false if event was not registered | |
func (d *Dispatcher) Dispatch(event Event) bool { | |
d.lock.RLock() | |
defer d.lock.RUnlock() | |
eventType := reflect.TypeOf(event).Elem() | |
if listeners, ok := d.handlers[eventType]; ok { | |
for _, listener := range listeners { | |
listener.TrySend(reflect.ValueOf(event.Clone()).Elem()) | |
} | |
return true | |
} | |
return false | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment