mirror of
https://github.com/libp2p/go-eventbus.git
synced 2026-08-23 15:33:28 +08:00
Compare commits
10 Commits
feat/bette
...
fix/with-n
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b280b5c1b | ||
|
|
df5be7d7dd | ||
|
|
0c299185af | ||
|
|
12782426ed | ||
|
|
9c3fe0580a | ||
|
|
0976d4b358 | ||
|
|
61257f90a2 | ||
|
|
454cbe5497 | ||
|
|
3fd76c7eac | ||
|
|
525a0e67fe |
59
basic.go
59
basic.go
@@ -51,7 +51,7 @@ func NewBus() event.Bus {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *basicBus) withNode(typ reflect.Type, cb func(*node), async func(*node)) error {
|
||||
func (b *basicBus) withNode(typ reflect.Type, cb func(*node), async func(*node)) {
|
||||
b.lk.Lock()
|
||||
|
||||
n, ok := b.nodes[typ]
|
||||
@@ -65,12 +65,14 @@ func (b *basicBus) withNode(typ reflect.Type, cb func(*node), async func(*node))
|
||||
|
||||
cb(n)
|
||||
|
||||
go func() {
|
||||
defer n.lk.Unlock()
|
||||
async(n)
|
||||
}()
|
||||
|
||||
return nil
|
||||
if async == nil {
|
||||
n.lk.Unlock()
|
||||
} else {
|
||||
go func() {
|
||||
defer n.lk.Unlock()
|
||||
async(n)
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *basicBus) tryDropNode(typ reflect.Type) {
|
||||
@@ -104,9 +106,21 @@ func (s *sub) Out() <-chan interface{} {
|
||||
}
|
||||
|
||||
func (s *sub) Close() error {
|
||||
close(s.ch)
|
||||
stop := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-s.ch:
|
||||
case <-stop:
|
||||
close(s.ch)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
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 +128,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(stop)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -129,7 +147,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,14 +166,16 @@ func (b *basicBus) Subscribe(evtTypes interface{}, opts ...event.SubscriptionOpt
|
||||
dropper: b.tryDropNode,
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if typ.Kind() != reflect.Ptr {
|
||||
return nil, errors.New("subscribe called with non-pointer type")
|
||||
}
|
||||
|
||||
err = b.withNode(typ.Elem(), func(n *node) {
|
||||
b.withNode(typ.Elem(), func(n *node) {
|
||||
n.sinks = append(n.sinks, out.ch)
|
||||
out.nodes[i] = n
|
||||
}, func(n *node) {
|
||||
@@ -184,6 +204,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
|
||||
@@ -196,11 +217,11 @@ func (b *basicBus) Emitter(evtType interface{}, opts ...event.EmitterOpt) (e eve
|
||||
}
|
||||
typ = typ.Elem()
|
||||
|
||||
err = b.withNode(typ, func(n *node) {
|
||||
b.withNode(typ, func(n *node) {
|
||||
atomic.AddInt32(&n.nEmitters, 1)
|
||||
n.keepLast = n.keepLast || settings.makeStateful
|
||||
e = &emitter{n: n, typ: typ, dropper: b.tryDropNode}
|
||||
}, func(_ *node) {})
|
||||
}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -229,9 +250,9 @@ 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()
|
||||
|
||||
@@ -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