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
100
util/eventbus/client.go
Normal file
100
util/eventbus/client.go
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package eventbus
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"tailscale.com/util/set"
|
||||
)
|
||||
|
||||
// A Client can publish and subscribe to events on its attached
|
||||
// bus. See [Publish] to publish events, and [Subscribe] to receive
|
||||
// events.
|
||||
//
|
||||
// Subscribers that share the same client receive events one at a
|
||||
// time, in the order they were published.
|
||||
type Client struct {
|
||||
name string
|
||||
bus *Bus
|
||||
|
||||
mu sync.Mutex
|
||||
pub set.Set[publisher]
|
||||
sub *subscribeState // Lazily created on first subscribe
|
||||
}
|
||||
|
||||
// Close closes the client. Implicitly closes all publishers and
|
||||
// subscribers obtained from this client.
|
||||
func (c *Client) Close() {
|
||||
var (
|
||||
pub set.Set[publisher]
|
||||
sub *subscribeState
|
||||
)
|
||||
|
||||
c.mu.Lock()
|
||||
pub, c.pub = c.pub, nil
|
||||
sub, c.sub = c.sub, nil
|
||||
c.mu.Unlock()
|
||||
|
||||
if sub != nil {
|
||||
sub.close()
|
||||
}
|
||||
for p := range pub {
|
||||
p.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) subscribeState() *subscribeState {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.sub == nil {
|
||||
c.sub = newSubscribeState(c)
|
||||
}
|
||||
return c.sub
|
||||
}
|
||||
|
||||
func (c *Client) addPublisher(pub publisher) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.pub.Add(pub)
|
||||
}
|
||||
|
||||
func (c *Client) deletePublisher(pub publisher) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.pub.Delete(pub)
|
||||
}
|
||||
|
||||
func (c *Client) addSubscriber(t reflect.Type, s *subscribeState) {
|
||||
c.bus.subscribe(t, s)
|
||||
}
|
||||
|
||||
func (c *Client) deleteSubscriber(t reflect.Type, s *subscribeState) {
|
||||
c.bus.unsubscribe(t, s)
|
||||
}
|
||||
|
||||
func (c *Client) publish() chan<- any {
|
||||
return c.bus.write
|
||||
}
|
||||
|
||||
func (c *Client) shouldPublish(t reflect.Type) bool {
|
||||
return c.bus.shouldPublish(t)
|
||||
}
|
||||
|
||||
// Subscribe requests delivery of events of type T through the given
|
||||
// Queue. Panics if the queue already has a subscriber for T.
|
||||
func Subscribe[T any](c *Client) *Subscriber[T] {
|
||||
return newSubscriber[T](c.subscribeState())
|
||||
}
|
||||
|
||||
// Publisher returns a publisher for event type T using the given
|
||||
// client.
|
||||
func Publish[T any](c *Client) *Publisher[T] {
|
||||
ret := newPublisher[T](c)
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.pub.Add(ret)
|
||||
return ret
|
||||
}
|
Reference in New Issue
Block a user