diff --git a/bus.go b/bus.go index 81aa338..7b5eb27 100644 --- a/bus.go +++ b/bus.go @@ -1,7 +1,5 @@ package events -import "sync" - type Bus struct { topics map[string]*topic } @@ -10,11 +8,11 @@ func New() *Bus { return &Bus{topics: make(map[string]*topic)} } -func (d *Bus) On(event string, receiver interface{}) *Subscription { +func (d *Bus) On(event string, receiver interface{}) { // TODO: make thread safe t, hasTopic := d.topics[event] if !hasTopic { - t = &topic{mutex: sync.Mutex{}, head: nil, tail: nil} + t = &topic{} d.topics[event] = t } @@ -24,7 +22,6 @@ func (d *Bus) On(event string, receiver interface{}) *Subscription { } t.addSubscriber(sub) - return sub } func (d *Bus) Fire(event string, args ...interface{}) { @@ -40,30 +37,9 @@ func (d *Bus) TryFire(event string, args ...interface{}) error { preparedArgs := prepareArgs(args) - topic.mutex.Lock() - var head = topic.head - topic.mutex.Unlock() - var errs []error - var lastSub *Subscription = nil - for sub := head; sub != nil; sub = sub.next { - // Remove unsubscribers - if sub.remove { - topic.mutex.Lock() - if lastSub == nil { - topic.head = sub.next - } else { - lastSub.next = sub.next - } - if topic.tail == sub { - topic.tail = lastSub - } - topic.mutex.Unlock() - continue - } - lastSub = sub - + for sub := topic.head; sub != nil; sub = sub.next { err := sub.handler.invoke(preparedArgs) if err != nil { errs = append(errs, err) @@ -80,16 +56,11 @@ func (d *Bus) TryFire(event string, args ...interface{}) error { } type topic struct { - // mutex protects writes to the head and tail - mutex sync.Mutex - head *Subscription - tail *Subscription + head *subscription + tail *subscription } -func (t *topic) addSubscriber(sub *Subscription) { - t.mutex.Lock() - defer t.mutex.Unlock() - +func (t *topic) addSubscriber(sub *subscription) { if t.head == nil { t.head = sub } @@ -99,20 +70,15 @@ func (t *topic) addSubscriber(sub *Subscription) { t.tail = sub } -type Subscription struct { - remove bool +type subscription struct { handler receiptHandler - next *Subscription + next *subscription } -func newSubscriptionFromFunc(fn interface{}) (*Subscription, error) { +func newSubscriptionFromFunc(fn interface{}) (*subscription, error) { handler, err := newReceiptHandler(fn) if err != nil { return nil, err } - return &Subscription{handler: handler, next: nil}, nil -} - -func (s *Subscription) Unsubscribe() { - s.remove = true + return &subscription{handler: handler, next: nil}, nil } diff --git a/bus_test.go b/bus_test.go index 23dedc4..808fa05 100644 --- a/bus_test.go +++ b/bus_test.go @@ -52,116 +52,6 @@ func TestFire(t *testing.T) { }) } -func TestUnsubscribe(t *testing.T) { - t.Run("should remove subscription 1", func(t *testing.T) { - fired := make([]int, 3) - - d := New() - s1 := d.On("event", func() { fired[0]++ }) - s2 := d.On("event", func() { fired[1]++ }) - s3 := d.On("event", func() { fired[2]++ }) - - d.Fire("event") - assert.Equal(t, []int{1, 1, 1}, fired) - - s1.Unsubscribe() - d.Fire("event") - assert.Equal(t, []int{1, 2, 2}, fired) - - s2.Unsubscribe() - d.Fire("event") - assert.Equal(t, []int{1, 2, 3}, fired) - - s3.Unsubscribe() - d.Fire("event") - assert.Equal(t, []int{1, 2, 3}, fired) - }) - - t.Run("should remove subscription 2", func(t *testing.T) { - fired := make([]int, 3) - - d := New() - s1 := d.On("event", func() { fired[0]++ }) - s2 := d.On("event", func() { fired[1]++ }) - s3 := d.On("event", func() { fired[2]++ }) - - d.Fire("event") - assert.Equal(t, []int{1, 1, 1}, fired) - - s3.Unsubscribe() - d.Fire("event") - assert.Equal(t, []int{2, 2, 1}, fired) - - s2.Unsubscribe() - d.Fire("event") - assert.Equal(t, []int{3, 2, 1}, fired) - - s1.Unsubscribe() - d.Fire("event") - assert.Equal(t, []int{3, 2, 1}, fired) - }) - - t.Run("should remove subscription 3", func(t *testing.T) { - fired := make([]int, 3) - - d := New() - s1 := d.On("event", func() { fired[0]++ }) - s2 := d.On("event", func() { fired[1]++ }) - s3 := d.On("event", func() { fired[2]++ }) - - d.Fire("event") - assert.Equal(t, []int{1, 1, 1}, fired) - - s2.Unsubscribe() - d.Fire("event") - assert.Equal(t, []int{2, 1, 2}, fired) - - s1.Unsubscribe() - d.Fire("event") - assert.Equal(t, []int{2, 1, 3}, fired) - - s3.Unsubscribe() - d.Fire("event") - assert.Equal(t, []int{2, 1, 3}, fired) - }) - - t.Run("should support new subscribers subscription", func(t *testing.T) { - fired := make([]int, 3) - - d := New() - d.On("event", func() { fired[0]++ }) - s2 := d.On("event", func() { fired[1]++ }) - - d.Fire("event") - assert.Equal(t, []int{1, 1, 0}, fired) - - s2.Unsubscribe() - d.Fire("event") - assert.Equal(t, []int{2, 1, 0}, fired) - - s3 := d.On("event", func() { fired[2]++ }) - s2.Unsubscribe() - d.Fire("event") - assert.Equal(t, []int{3, 1, 1}, fired) - - s3.Unsubscribe() - d.Fire("event") - assert.Equal(t, []int{4, 1, 1}, fired) - }) - - t.Run("should nop if no events", func(t *testing.T) { - fired := false - - d := New() - s1 := d.On("event", func() { fired = true }) - s1.Unsubscribe() - s1.Unsubscribe() - - d.Fire("event") - assert.False(t, fired) - }) -} - func TestTryFire(t *testing.T) { errVal := errors.New("bang") diff --git a/go.mod b/go.mod index 6b2ff50..e6c9285 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,5 @@ -module lmika.dev/pkg/events +module github.com/lmika/events -go 1.24 +go 1.14 -require github.com/stretchr/testify v1.9.0 - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require github.com/stretchr/testify v1.9.0 // indirect diff --git a/go.sum b/go.sum index 60ce688..f62753e 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,22 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=