mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-22 11:01:54 +00:00
util/eventbus: rework to have a Client abstraction
The Client carries both publishers and subscribers for a single actor. This makes the APIs for publish and subscribe look more similar, and this structure is a better fit for upcoming debug facilities. Updates #15160 Signed-off-by: David Anderson <dave@tailscale.com>
This commit is contained in:

committed by
Dave Anderson

parent
f840aad49e
commit
3e18434595
@@ -11,35 +11,41 @@ import (
|
||||
// publisher is a uniformly typed wrapper around Publisher[T], so that
|
||||
// debugging facilities can look at active publishers.
|
||||
type publisher interface {
|
||||
publisherName() string
|
||||
publishType() reflect.Type
|
||||
Close()
|
||||
}
|
||||
|
||||
// A Publisher publishes events on the bus.
|
||||
// A Publisher publishes typed events on a bus.
|
||||
type Publisher[T any] struct {
|
||||
bus *Bus
|
||||
name string
|
||||
client *Client
|
||||
stopCtx context.Context
|
||||
stop context.CancelFunc
|
||||
}
|
||||
|
||||
// PublisherOf returns a publisher for event type T on the given bus.
|
||||
//
|
||||
// The publisher's name should be a short, human-readable string that
|
||||
// identifies this event publisher. The name is only visible through
|
||||
// debugging APIs.
|
||||
func PublisherOf[T any](b *Bus, name string) *Publisher[T] {
|
||||
func newPublisher[T any](c *Client) *Publisher[T] {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ret := &Publisher[T]{
|
||||
bus: b,
|
||||
name: name,
|
||||
client: c,
|
||||
stopCtx: ctx,
|
||||
stop: cancel,
|
||||
}
|
||||
b.addPublisher(ret)
|
||||
c.addPublisher(ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p *Publisher[T]) publisherName() string { return p.name }
|
||||
// Close closes the publisher.
|
||||
//
|
||||
// Calls to Publish after Close silently do nothing.
|
||||
func (p *Publisher[T]) Close() {
|
||||
// Just unblocks any active calls to Publish, no other
|
||||
// synchronization needed.
|
||||
p.stop()
|
||||
p.client.deletePublisher(p)
|
||||
}
|
||||
|
||||
func (p *Publisher[T]) publishType() reflect.Type {
|
||||
return reflect.TypeFor[T]()
|
||||
}
|
||||
|
||||
// Publish publishes event v on the bus.
|
||||
func (p *Publisher[T]) Publish(v T) {
|
||||
@@ -48,32 +54,21 @@ func (p *Publisher[T]) Publish(v T) {
|
||||
select {
|
||||
case <-p.stopCtx.Done():
|
||||
return
|
||||
case <-p.bus.stop.WaitChan():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
select {
|
||||
case p.bus.write <- v:
|
||||
case p.client.publish() <- v:
|
||||
case <-p.stopCtx.Done():
|
||||
case <-p.bus.stop.WaitChan():
|
||||
}
|
||||
}
|
||||
|
||||
// ShouldPublish reports whether anyone is subscribed to events of
|
||||
// type T.
|
||||
// ShouldPublish reports whether anyone is subscribed to the events
|
||||
// that this publisher emits.
|
||||
//
|
||||
// ShouldPublish can be used to skip expensive event construction if
|
||||
// nobody seems to care. Publishers must not assume that someone will
|
||||
// definitely receive an event if ShouldPublish returns true.
|
||||
func (p *Publisher[T]) ShouldPublish() bool {
|
||||
dests := p.bus.dest(reflect.TypeFor[T]())
|
||||
return len(dests) > 0
|
||||
}
|
||||
|
||||
// Close closes the publisher, indicating that no further events will
|
||||
// be published with it.
|
||||
func (p *Publisher[T]) Close() {
|
||||
p.stop()
|
||||
p.bus.deletePublisher(p)
|
||||
return p.client.shouldPublish(reflect.TypeFor[T]())
|
||||
}
|
||||
|
Reference in New Issue
Block a user