diff --git a/README.md b/README.md index 305803e..fe325cc 100644 --- a/README.md +++ b/README.md @@ -39,10 +39,10 @@ func main() { } } - emitter.On("hello", hello). - On("count", count). - Emit("hello", "world"). - Emit("count", 5) + emitter.On("hello", hello) + emitter.On("count", count) + emitter.Emit("hello", "world") + emitter.Emit("count", 5) } ``` diff --git a/emitter.go b/emitter.go index f529173..a0d85f5 100644 --- a/emitter.go +++ b/emitter.go @@ -18,27 +18,40 @@ var ErrNoneFunction = errors.New("Kind of Value for listener is not Func.") // RecoveryListener ... type RecoveryListener func(interface{}, interface{}, error) +// ListenerHandle is an opaque reference to a previously added listener. You need the handle to remove the listener. +type ListenerHandle uint32 + +type listenerRecord struct { + fn reflect.Value + handle ListenerHandle + isOnce bool +} + // Emitter ... type Emitter struct { // Mutex to prevent race conditions within the Emitter. *sync.Mutex + // Unique counter to allocate handles + nextHandle ListenerHandle // Map of event to a slice of listener function's reflect Values. - events map[interface{}][]reflect.Value + events map[interface{}][]listenerRecord // Optional RecoveryListener to call when a panic occurs. recoverer RecoveryListener // Maximum listeners for debugging potential memory leaks. maxListeners int - // Map used to remove Listeners wrapped in a Once func - onces map[reflect.Value]reflect.Value } // AddListener appends the listener argument to the event arguments slice // in the Emitter's events map. If the number of listeners for an event // is greater than the Emitter's maximum listeners then a warning is printed. -// If the relect Value of the listener does not have a Kind of Func then +// If the reflect Value of the listener does not have a Kind of Func then // AddListener panics. If a RecoveryListener has been set then it is called // recovering from the panic. -func (emitter *Emitter) AddListener(event, listener interface{}) *Emitter { +func (emitter *Emitter) AddListener(event, listener interface{}) ListenerHandle { + return emitter.addListener(event,listener,false) +} + +func (emitter *Emitter) addListener(event, listener interface{}, isOnce bool) ListenerHandle { emitter.Lock() defer emitter.Unlock() @@ -57,13 +70,16 @@ func (emitter *Emitter) AddListener(event, listener interface{}) *Emitter { "number of listeners of %d.\n", event, emitter.maxListeners) } - emitter.events[event] = append(emitter.events[event], fn) + emitter.nextHandle = emitter.nextHandle + 1 + handle := emitter.nextHandle - return emitter + emitter.events[event] = append(emitter.events[event], listenerRecord{fn,handle, isOnce}) + + return handle } // On is an alias for AddListener. -func (emitter *Emitter) On(event, listener interface{}) *Emitter { +func (emitter *Emitter) On(event, listener interface{}) ListenerHandle { return emitter.AddListener(event, listener) } @@ -71,42 +87,35 @@ func (emitter *Emitter) On(event, listener interface{}) *Emitter { // in the Emitter's events map. If the reflect Value of the listener does not // have a Kind of Func then RemoveListener panics. If a RecoveryListener has // been set then it is called after recovering from the panic. -func (emitter *Emitter) RemoveListener(event, listener interface{}) *Emitter { +func (emitter *Emitter) RemoveListener(event interface{}, listenerHandle ListenerHandle) { emitter.Lock() defer emitter.Unlock() - fn := reflect.ValueOf(listener) - - if reflect.Func != fn.Kind() { - if nil == emitter.recoverer { - panic(ErrNoneFunction) - } else { - emitter.recoverer(event, listener, ErrNoneFunction) - } - } - if events, ok := emitter.events[event]; ok { - if _, ok = emitter.onces[fn]; ok { - fn = emitter.onces[fn] + l := len(events) + if l == 0 { + return } - newEvents := []reflect.Value{} + newEvents := make([]listenerRecord,0,l - 1) - for _, listener := range events { - if fn.Pointer() != listener.Pointer() { - newEvents = append(newEvents, listener) + for _, listenerRec := range events { + if listenerHandle != listenerRec.handle { + newEvents = append(newEvents, listenerRec) } } - emitter.events[event] = newEvents + if len(newEvents) > 0 { + emitter.events[event] = newEvents + } else { + delete(emitter.events,event) + } } - - return emitter } // Off is an alias for RemoveListener. -func (emitter *Emitter) Off(event, listener interface{}) *Emitter { - return emitter.RemoveListener(event, listener) +func (emitter *Emitter) Off(event, listener ListenerHandle) { + emitter.RemoveListener(event, listener) } // Once generates a new function which invokes the supplied listener @@ -114,38 +123,8 @@ func (emitter *Emitter) Off(event, listener interface{}) *Emitter { // in the Emitter's events map. If the reflect Value of the listener // does not have a Kind of Func then Once panics. If a RecoveryListener // has been set then it is called after recovering from the panic. -func (emitter *Emitter) Once(event, listener interface{}) *Emitter { - fn := reflect.ValueOf(listener) - - if reflect.Func != fn.Kind() { - if nil == emitter.recoverer { - panic(ErrNoneFunction) - } else { - emitter.recoverer(event, listener, ErrNoneFunction) - } - } - - var run func(...interface{}) - - run = func(arguments ...interface{}) { - defer emitter.RemoveListener(event, run) - - var values []reflect.Value - - for i := 0; i < len(arguments); i++ { - values = append(values, reflect.ValueOf(arguments[i])) - } - - fn.Call(values) - } - - // Lock before changing onces - emitter.Lock() - emitter.onces[fn] = reflect.ValueOf(run) - emitter.Unlock() - - emitter.AddListener(event, run) - return emitter +func (emitter *Emitter) Once(event, listener interface{}) ListenerHandle { + return emitter.addListener(event,listener,true) } // Emit attempts to use the reflect package to Call each listener stored @@ -156,7 +135,7 @@ func (emitter *Emitter) Once(event, listener interface{}) *Emitter { // the panic. func (emitter *Emitter) Emit(event interface{}, arguments ...interface{}) *Emitter { var ( - listeners []reflect.Value + listeners []listenerRecord ok bool ) @@ -180,10 +159,12 @@ func (emitter *Emitter) Emit(event interface{}, arguments ...interface{}) *Emitt wg.Add(len(listeners)) - for _, fn := range listeners { - go func(fn reflect.Value) { + for _, listenerRec := range listeners { + go func(listenerRec listenerRecord) { defer wg.Done() + fn := listenerRec.fn + // Recover from potential panics, supplying them to a // RecoveryListener if one has been set, else allowing // the panic to occur. @@ -206,8 +187,12 @@ func (emitter *Emitter) Emit(event interface{}, arguments ...interface{}) *Emitt } } + if listenerRec.isOnce { + emitter.RemoveListener(event,listenerRec.handle) + } + fn.Call(values) - }(fn) + }(listenerRec) } wg.Wait() @@ -222,7 +207,7 @@ func (emitter *Emitter) Emit(event interface{}, arguments ...interface{}) *Emitt // the panic. func (emitter *Emitter) EmitSync(event interface{}, arguments ...interface{}) *Emitter { var ( - listeners []reflect.Value + listeners []listenerRecord ok bool ) @@ -242,7 +227,9 @@ func (emitter *Emitter) EmitSync(event interface{}, arguments ...interface{}) *E // with Once can aquire the mutex for removal. emitter.Unlock() - for _, fn := range listeners { + for _, listenerRec := range listeners { + fn := listenerRec.fn + // Recover from potential panics, supplying them to a // RecoveryListener if one has been set, else allowing // the panic to occur. @@ -265,6 +252,10 @@ func (emitter *Emitter) EmitSync(event interface{}, arguments ...interface{}) *E } } + if listenerRec.isOnce { + emitter.RemoveListener(event,listenerRec.handle) + } + fn.Call(values) } @@ -307,8 +298,7 @@ func (emitter *Emitter) GetListenerCount(event interface{}) (count int) { func NewEmitter() (emitter *Emitter) { emitter = new(Emitter) emitter.Mutex = new(sync.Mutex) - emitter.events = make(map[interface{}][]reflect.Value) + emitter.events = make(map[interface{}][]listenerRecord) emitter.maxListeners = DefaultMaxListeners - emitter.onces = make(map[reflect.Value]reflect.Value) return } diff --git a/emitter_test.go b/emitter_test.go index 8074ea9..7269e97 100644 --- a/emitter_test.go +++ b/emitter_test.go @@ -7,8 +7,8 @@ import ( func TestAddListener(t *testing.T) { event := "test" - emitter := NewEmitter(). - AddListener(event, func() {}) + emitter := NewEmitter() + emitter.AddListener(event, func() {}) if 1 != len(emitter.events[event]) { t.Error("Failed to add listener to the emitter.") @@ -19,9 +19,9 @@ func TestEmit(t *testing.T) { event := "test" flag := true - NewEmitter(). - AddListener(event, func() { flag = !flag }). - Emit(event) + emitter := NewEmitter() + emitter.AddListener(event, func() { flag = !flag }) + emitter.Emit(event) if flag { t.Error("Emit failed to call listener to unset flag.") @@ -32,9 +32,9 @@ func TestEmitSync(t *testing.T) { event := "test" flag := true - NewEmitter(). - AddListener(event, func() { flag = !flag }). - EmitSync(event) + emitter := NewEmitter() + emitter.AddListener(event, func() { flag = !flag }) + emitter.EmitSync(event) if flag { t.Error("EmitSync failed to call listener to unset flag.") @@ -45,14 +45,14 @@ func TestEmitWithMultipleListeners(t *testing.T) { event := "test" invoked := 0 - NewEmitter(). - AddListener(event, func() { - invoked = invoked + 1 - }). - AddListener(event, func() { - invoked = invoked + 1 - }). - Emit(event) + emitter := NewEmitter() + emitter.AddListener(event, func() { + invoked = invoked + 1 + }) + emitter.AddListener(event, func() { + invoked = invoked + 1 + }) + emitter.Emit(event) if invoked != 2 { t.Error("Emit failed to call all listeners.") @@ -63,9 +63,9 @@ func TestRemoveListener(t *testing.T) { event := "test" listener := func() {} - emitter := NewEmitter(). - AddListener(event, listener). - RemoveListener(event, listener) + emitter := NewEmitter() + handle := emitter.AddListener(event, listener) + emitter.RemoveListener(event, handle) if 0 != len(emitter.events[event]) { t.Error("Failed to remove listener from the emitter.") @@ -76,10 +76,10 @@ func TestOnce(t *testing.T) { event := "test" flag := true - NewEmitter(). - Once(event, func() { flag = !flag }). - Emit(event). - Emit(event) + emitter := NewEmitter() + emitter.Once(event, func() { flag = !flag }) + emitter.Emit(event) + emitter.Emit(event) if flag { t.Error("Once called listener multiple times reseting the flag.") @@ -90,10 +90,10 @@ func TestRecoveryWith(t *testing.T) { event := "test" flag := true - NewEmitter(). - AddListener(event, func() { panic(event) }). - RecoverWith(func(event, listener interface{}, err error) { flag = !flag }). - Emit(event) + emitter := NewEmitter() + emitter.AddListener(event, func() { panic(event) }) + emitter.RecoverWith(func(event, listener interface{}, err error) { flag = !flag }) + emitter.Emit(event) if flag { t.Error("Listener supplied to RecoverWith was not called to unset flag on panic.") @@ -105,10 +105,10 @@ func TestRemoveOnce(t *testing.T) { flag := false fn := func() { flag = !flag } - NewEmitter(). - Once(event, fn). - RemoveListener(event, fn). - Emit(event) + emitter := NewEmitter() + handle := emitter.Once(event, fn) + emitter.RemoveListener(event, handle) + emitter.Emit(event) if flag { t.Error("Failed to remove Listener for Once") @@ -118,8 +118,8 @@ func TestRemoveOnce(t *testing.T) { func TestCountListener(t *testing.T) { event := "test" - emitter := NewEmitter(). - AddListener(event, func() {}) + emitter := NewEmitter() + emitter.AddListener(event, func() {}) if 1 != emitter.GetListenerCount(event) { t.Error("Failed to get listener count from emitter.") @@ -137,9 +137,10 @@ func (*SomeType) Receiver(evt string) {} func TestRemoveStructMethod(t *testing.T) { event := "test" listener := &SomeType{} - emitter := NewEmitter().AddListener(event, listener.Receiver) + emitter := NewEmitter() + handle := emitter.AddListener(event, listener.Receiver) - emitter.RemoveListener(event, listener.Receiver) + emitter.RemoveListener(event, handle) if 0 != emitter.GetListenerCount(event) { t.Error("Failed to remove listener from emitter.") } @@ -150,5 +151,15 @@ func TestRemoveDoubleListener(t *testing.T) { fn1 := func() {} - NewEmitter().On(event, fn1).On(event, fn1).RemoveListener(event, fn1) + emitter := NewEmitter() + handle1 := emitter.On(event, fn1) + handle2 := emitter.On(event, fn1) + emitter.RemoveListener(event, handle1) + if 1 != emitter.GetListenerCount(event) { + t.Error("Should have removed just one listener.") + } + emitter.RemoveListener(event, handle2) + if 0 != emitter.GetListenerCount(event) { + t.Error("Should have removed both listeners.") + } }