mirror of
https://github.com/libp2p/go-eventbus.git
synced 2026-08-23 15:33:28 +08:00
Compare commits
12 Commits
feat/bette
...
fix/16
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
25d54bbbec | ||
|
|
04058af20a | ||
|
|
a77f09c820 | ||
|
|
df5be7d7dd | ||
|
|
0c299185af | ||
|
|
12782426ed | ||
|
|
9c3fe0580a | ||
|
|
0976d4b358 | ||
|
|
61257f90a2 | ||
|
|
454cbe5497 | ||
|
|
3fd76c7eac | ||
|
|
525a0e67fe |
44
basic.go
44
basic.go
@@ -104,9 +104,16 @@ func (s *sub) Out() <-chan interface{} {
|
||||
}
|
||||
|
||||
func (s *sub) Close() error {
|
||||
close(s.ch)
|
||||
go func() {
|
||||
// drain the event channel, will return when closed and drained.
|
||||
// this is necessary to unblock publishes to this channel.
|
||||
for range s.ch {
|
||||
}
|
||||
}()
|
||||
|
||||
for _, n := range s.nodes {
|
||||
n.lk.Lock()
|
||||
|
||||
for i := 0; i < len(n.sinks); i++ {
|
||||
if n.sinks[i] == s.ch {
|
||||
n.sinks[i], n.sinks[len(n.sinks)-1] = n.sinks[len(n.sinks)-1], nil
|
||||
@@ -114,12 +121,16 @@ func (s *sub) Close() error {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
tryDrop := len(n.sinks) == 0 && atomic.LoadInt32(&n.nEmitters) == 0
|
||||
|
||||
n.lk.Unlock()
|
||||
|
||||
if tryDrop {
|
||||
s.dropper(n.typ)
|
||||
}
|
||||
}
|
||||
close(s.ch)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -129,7 +140,7 @@ var _ event.Subscription = (*sub)(nil)
|
||||
// publishers to get blocked. CancelFunc is guaranteed to return after last send
|
||||
// to the channel
|
||||
func (b *basicBus) Subscribe(evtTypes interface{}, opts ...event.SubscriptionOpt) (_ event.Subscription, err error) {
|
||||
var settings subSettings
|
||||
settings := subSettings(subSettingsDefault)
|
||||
for _, opt := range opts {
|
||||
if err := opt(&settings); err != nil {
|
||||
return nil, err
|
||||
@@ -148,19 +159,21 @@ func (b *basicBus) Subscribe(evtTypes interface{}, opts ...event.SubscriptionOpt
|
||||
dropper: b.tryDropNode,
|
||||
}
|
||||
|
||||
for i, etyp := range types {
|
||||
typ := reflect.TypeOf(etyp)
|
||||
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
for _, etyp := range types {
|
||||
if reflect.TypeOf(etyp).Kind() != reflect.Ptr {
|
||||
return nil, errors.New("subscribe called with non-pointer type")
|
||||
}
|
||||
}
|
||||
|
||||
for i, etyp := range types {
|
||||
typ := reflect.TypeOf(etyp)
|
||||
|
||||
err = b.withNode(typ.Elem(), func(n *node) {
|
||||
n.sinks = append(n.sinks, out.ch)
|
||||
out.nodes[i] = n
|
||||
}, func(n *node) {
|
||||
if n.keepLast {
|
||||
l := n.last.Load()
|
||||
l := n.last
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
@@ -184,6 +197,7 @@ func (b *basicBus) Subscribe(evtTypes interface{}, opts ...event.SubscriptionOpt
|
||||
// emit(EventT{})
|
||||
func (b *basicBus) Emitter(evtType interface{}, opts ...event.EmitterOpt) (e event.Emitter, err error) {
|
||||
var settings emitterSettings
|
||||
|
||||
for _, opt := range opts {
|
||||
if err := opt(&settings); err != nil {
|
||||
return nil, err
|
||||
@@ -209,7 +223,7 @@ func (b *basicBus) Emitter(evtType interface{}, opts ...event.EmitterOpt) (e eve
|
||||
|
||||
type node struct {
|
||||
// Note: make sure to NEVER lock basicBus.lk when this lock is held
|
||||
lk sync.RWMutex
|
||||
lk sync.Mutex
|
||||
|
||||
typ reflect.Type
|
||||
|
||||
@@ -217,7 +231,7 @@ type node struct {
|
||||
nEmitters int32
|
||||
|
||||
keepLast bool
|
||||
last atomic.Value
|
||||
last interface{}
|
||||
|
||||
sinks []chan interface{}
|
||||
}
|
||||
@@ -229,18 +243,18 @@ func newNode(typ reflect.Type) *node {
|
||||
}
|
||||
|
||||
func (n *node) emit(event interface{}) {
|
||||
eval := reflect.ValueOf(event)
|
||||
if eval.Type() != n.typ {
|
||||
panic(fmt.Sprintf("Emit called with wrong type. expected: %s, got: %s", n.typ, eval.Type()))
|
||||
typ := reflect.TypeOf(event)
|
||||
if typ != n.typ {
|
||||
panic(fmt.Sprintf("Emit called with wrong type. expected: %s, got: %s", n.typ, typ))
|
||||
}
|
||||
|
||||
n.lk.RLock()
|
||||
n.lk.Lock()
|
||||
if n.keepLast {
|
||||
n.last.Store(event)
|
||||
n.last = event
|
||||
}
|
||||
|
||||
for _, ch := range n.sinks {
|
||||
ch <- event
|
||||
}
|
||||
n.lk.RUnlock()
|
||||
n.lk.Unlock()
|
||||
}
|
||||
|
||||
@@ -25,6 +25,17 @@ func (EventA) String() string {
|
||||
return "Oh, Hello"
|
||||
}
|
||||
|
||||
func TestDefaultSubIsBuffered(t *testing.T) {
|
||||
bus := NewBus()
|
||||
s, err := bus.Subscribe(new(EventA))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cap(s.(*sub).ch) == 0 {
|
||||
t.Fatalf("without any options subscribe should be buffered. was %d", cap(s.(*sub).ch))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmit(t *testing.T) {
|
||||
bus := NewBus()
|
||||
sub, err := bus.Subscribe(new(EventA))
|
||||
@@ -297,6 +308,49 @@ func TestStateful(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloseBlocking(t *testing.T) {
|
||||
bus := NewBus()
|
||||
em, err := bus.Emitter(new(EventB))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sub, err := bus.Subscribe(new(EventB))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
em.Emit(EventB(159))
|
||||
}()
|
||||
|
||||
time.Sleep(10 * time.Millisecond) // make sure that emit is blocked
|
||||
|
||||
sub.Close() // cancel sub
|
||||
}
|
||||
|
||||
func panicOnTimeout(d time.Duration) {
|
||||
<-time.After(d)
|
||||
panic("timeout reached")
|
||||
}
|
||||
|
||||
func TestSubFailFully(t *testing.T) {
|
||||
bus := NewBus()
|
||||
em, err := bus.Emitter(new(EventB))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = bus.Subscribe([]interface{}{new(EventB), 5})
|
||||
if err == nil || err.Error() != "subscribe called with non-pointer type" {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
go panicOnTimeout(5 * time.Second)
|
||||
|
||||
em.Emit(EventB(159)) // will hang if sub doesn't fail properly
|
||||
}
|
||||
|
||||
func testMany(t testing.TB, subs, emits, msgs int, stateful bool) {
|
||||
if race.WithRace() && subs+emits > 5000 {
|
||||
t.SkipNow()
|
||||
|
||||
Reference in New Issue
Block a user