Skip to content

Commit

Permalink
Merge pull request #2 from vansante/allowEmitEventsFromEvents
Browse files Browse the repository at this point in the history
Rewrite emitter to allow emitting events from within eventlisteners in non async mode
  • Loading branch information
vansante authored Jun 1, 2018
2 parents ea73e03 + c9b1809 commit d1e9ab5
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 90 deletions.
146 changes: 80 additions & 66 deletions emitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,170 +5,184 @@ import (
)

type Emitter struct {
sync.RWMutex

async bool
capturers []*Capturer
listeners map[EventType][]*Listener
listenerMutex sync.Mutex
listenersOnce map[EventType][]*Listener
}

// NewEmitter creates a new event emitter that implements the Observable interface.
// Async determines whether events listeners fire in separate goroutines or not.
func NewEmitter(async bool) (em *Emitter) {
em = &Emitter{
async: async,
listeners: make(map[EventType][]*Listener),
async: async,
listeners: make(map[EventType][]*Listener),
listenersOnce: make(map[EventType][]*Listener),
}
return em
}

// EmitEvent emits the given event to all listeners and capturers
func (em *Emitter) EmitEvent(event EventType, arguments ...interface{}) {
em.listenerMutex.Lock()
defer em.listenerMutex.Unlock()

removed := 0
var adjustedIndex int
for index := range em.listeners[event] {
adjustedIndex = index - removed

if em.async {
go em.listeners[event][adjustedIndex].handler(arguments...)
} else {
em.listeners[event][adjustedIndex].handler(arguments...)
}
// If we have no single listeners for this event, skip
if len(em.listenersOnce) > 0 {
// Get a full lock, we are changing a map
em.Lock()
// Copy the slice
listenersOnce := em.listenersOnce[event]
// Create new empty slice
em.listenersOnce[event] = make([]*Listener, 0)
em.Unlock()

// No lock needed, we are working with an inaccessible copy
em.emitListenerEvents(listenersOnce, arguments)
}

if em.listeners[event][adjustedIndex].once {
em.removeListenerAtIndex(event, adjustedIndex)
removed++
}
// If we have no listeners for this event, skip
if len(em.listeners[event]) > 0 {
em.RLock()
em.emitListenerEvents(em.listeners[event], arguments)
em.RUnlock()
}

removed = 0
for index := range em.capturers {
adjustedIndex = index - removed
// If we have no capturers, skip
if len(em.capturers) > 0 {
em.RLock()
em.emitCapturerEvents(em.capturers, event, arguments)
em.RUnlock()
}
}

func (em *Emitter) emitListenerEvents(listeners []*Listener, arguments []interface{}) {
for _, listener := range listeners {
if em.async {
go em.capturers[adjustedIndex].handler(event, arguments...)
} else {
em.capturers[adjustedIndex].handler(event, arguments...)
go listener.handler(arguments...)
continue
}
listener.handler(arguments...)
}
}

if em.capturers[adjustedIndex].once {
em.removeCapturerAtIndex(adjustedIndex)
removed++
func (em *Emitter) emitCapturerEvents(capturers []*Capturer, event EventType, arguments []interface{}) {
for _, capturer := range capturers {
if em.async {
go capturer.handler(event, arguments...)
continue
}
capturer.handler(event, arguments...)
}
}

// AddListener adds a listener for the given event type
func (em *Emitter) AddListener(event EventType, handler HandleFunc) (listener *Listener) {
em.listenerMutex.Lock()
defer em.listenerMutex.Unlock()
em.Lock()
defer em.Unlock()

listener = &Listener{
handler: handler,
once: false,
}
em.listeners[event] = append(em.listeners[event], listener)
return listener
}

// ListenOnce adds a listener for the given event type that removes itself after it has been fired once
func (em *Emitter) ListenOnce(event EventType, handler HandleFunc) (listener *Listener) {
em.listenerMutex.Lock()
defer em.listenerMutex.Unlock()
em.Lock()
defer em.Unlock()

listener = &Listener{
handler: handler,
once: true,
}
em.listeners[event] = append(em.listeners[event], listener)
em.listenersOnce[event] = append(em.listenersOnce[event], listener)
return listener
}

// AddCapturer adds an event capturer for all events
func (em *Emitter) AddCapturer(handler CaptureFunc) (capturer *Capturer) {
em.listenerMutex.Lock()
defer em.listenerMutex.Unlock()

capturer = &Capturer{
handler: handler,
once: false,
}
em.capturers = append(em.capturers, capturer)
return capturer
}

// CaptureOnce adds an event capturer for all events that removes itself after it has been fired once
func (em *Emitter) CaptureOnce(handler CaptureFunc) (capturer *Capturer) {
em.listenerMutex.Lock()
defer em.listenerMutex.Unlock()
em.Lock()
defer em.Unlock()

capturer = &Capturer{
handler: handler,
once: true,
}
em.capturers = append(em.capturers, capturer)
return capturer
}

// RemoveListener removes the registered given listener for the given event
func (em *Emitter) RemoveListener(event EventType, listener *Listener) {
em.listenerMutex.Lock()
defer em.listenerMutex.Unlock()
em.Lock()
defer em.Unlock()

for index, list := range em.listeners[event] {
if list == listener {
em.removeListenerAtIndex(event, index)
em.removeListenerAt(event, index)
return
}
}

// If it hasnt been found yet, remove from listeners once if present there
for index, list := range em.listenersOnce[event] {
if list == listener {
em.removeOnceListenerAt(event, index)
return
}
}
}

// RemoveAllListenersForEvent removes all registered listeners for a given event type
func (em *Emitter) RemoveAllListenersForEvent(event EventType) {
em.listenerMutex.Lock()
defer em.listenerMutex.Unlock()
em.Lock()
defer em.Unlock()

em.listeners[event] = make([]*Listener, 0)
}

// RemoveAllListeners removes all registered listeners for all event types
func (em *Emitter) RemoveAllListeners() {
em.listenerMutex.Lock()
defer em.listenerMutex.Unlock()
em.Lock()
defer em.Unlock()

em.listeners = make(map[EventType][]*Listener)
em.listenersOnce = make(map[EventType][]*Listener)
}

// RemoveCapturer removes the given capturer
func (em *Emitter) RemoveCapturer(capturer *Capturer) {
em.listenerMutex.Lock()
defer em.listenerMutex.Unlock()
em.Lock()
defer em.Unlock()

for index, capt := range em.capturers {
if capt == capturer {
em.removeCapturerAtIndex(index)
em.removeCapturerAt(index)
return
}
}
}

// RemoveAllCapturers removes all registered capturers
func (em *Emitter) RemoveAllCapturers() {
em.listenerMutex.Lock()
defer em.listenerMutex.Unlock()
em.Lock()
defer em.Unlock()

em.capturers = make([]*Capturer, 0)
}

func (em *Emitter) removeListenerAtIndex(event EventType, index int) {
func (em *Emitter) removeListenerAt(event EventType, index int) {
copy(em.listeners[event][index:], em.listeners[event][index+1:])
em.listeners[event][len(em.listeners[event])-1] = nil
em.listeners[event] = em.listeners[event][:len(em.listeners[event])-1]
}

func (em *Emitter) removeCapturerAtIndex(index int) {
func (em *Emitter) removeOnceListenerAt(event EventType, index int) {
copy(em.listenersOnce[event][index:], em.listenersOnce[event][index+1:])
em.listenersOnce[event][len(em.listenersOnce[event])-1] = nil
em.listenersOnce[event] = em.listenersOnce[event][:len(em.listenersOnce[event])-1]
}

func (em *Emitter) removeCapturerAt(index int) {
copy(em.capturers[index:], em.capturers[index+1:])
em.capturers[len(em.capturers)-1] = nil
em.capturers = em.capturers[:len(em.capturers)-1]
Expand Down
55 changes: 37 additions & 18 deletions emitter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ func testEmitter(t *testing.T, async bool) {
em = e
ob = e

var ASingle, AListener, capture, captureOnce int
var ASingle, AListener, capture int

listener := ob.AddListener("test event A", func(args ...interface{}) {
verifyArgs(t, args)
AListener++
})

ob.ListenOnce("test event A", func(args ...interface{}){
ob.ListenOnce("test event A", func(args ...interface{}) {
verifyArgs(t, args)
ASingle++
})
Expand All @@ -38,16 +38,6 @@ func testEmitter(t *testing.T, async bool) {
capture++
})

ob.CaptureOnce(func(event EventType, args ...interface{}) {
verifyArgs(t, args)

captureOnce++
if event != "test event A" {
t.Log("wrong event for captureOnce")
t.Fail()
}
})

em.EmitEvent("test event A", "test", 123, true)
em.EmitEvent("test event B", "test", 123, true)
em.EmitEvent("test event C", "test", 123, true)
Expand Down Expand Up @@ -78,15 +68,11 @@ func testEmitter(t *testing.T, async bool) {
t.Log("Capture all not triggered right", capture)
t.Fail()
}
if captureOnce != 1 {
t.Log("Capture once not triggered right", captureOnce)
t.Fail()
}
}

func verifyArgs(t *testing.T, args []interface{}) {
if len(args) != 3 {
t.Log("Too few arguments", args)
t.Logf("Too few arguments (%d) %#v", len(args), args)
t.Fail()
return
}
Expand All @@ -108,4 +94,37 @@ func verifyArgs(t *testing.T, args []interface{}) {
t.Log("Wrong argument for 3:true!")
t.Fail()
}
}
}

func TestEmitNonAsyncRecursive(t *testing.T) {
e := NewEmitter(false)

var rootFired int
e.AddListener("rootevent", func(args ...interface{}) {
rootFired++
e.EmitEvent("subevent", 1, 2, 3)
e.EmitEvent("subevent", 1, 2, 3)
})

var subFired int
e.AddListener("subevent", func(args ...interface{}) {
if len(args) != 3 {
t.Logf("Too few arguments (%d) %#v", len(args), args)
t.Fail()
return
}
subFired++
})

e.EmitEvent("rootevent", "test")

if rootFired != 1 {
t.Log("Root event all not triggered right", rootFired)
t.Fail()
}

if subFired != 2 {
t.Log("Sub event all not triggered right", subFired)
t.Fail()
}
}
12 changes: 6 additions & 6 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ package eventemitter

type EventType string

// HandleFunc is a handler function for a given event type
type HandleFunc func(arguments ...interface{})
// Listener is a container struct used to remove the listener
type Listener struct {
handler HandleFunc
once bool
}

type CaptureFunc func(eventName EventType, arguments ...interface{})
// CaptureFunc is a capturer function that can capture all emitted events
type CaptureFunc func(event EventType, arguments ...interface{})
// Capturer is a container struct used to remove the capturer
type Capturer struct {
handler CaptureFunc
once bool
}

// Observable describes an object that can be listened to by event listeners and capturers
Expand All @@ -22,11 +24,9 @@ type Observable interface {
ListenOnce(event EventType, handler HandleFunc) (listener *Listener)
// AddCapturer adds an event capturer for all events
AddCapturer(handler CaptureFunc) (capturer *Capturer)
// CaptureOnce adds an event capturer for all events that removes itself after it has been fired once
CaptureOnce(handler CaptureFunc) (capturer *Capturer)
// RemoveListener removes the registered given listener for the given event
RemoveListener(event EventType, listener *Listener)
// // RemoveCapturer removes the given capturer
// RemoveCapturer removes the given capturer
RemoveCapturer(capturer *Capturer)
}

Expand Down

0 comments on commit d1e9ab5

Please sign in to comment.