mirror of https://github.com/tailscale/tailscale/
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>pull/15211/head
parent
f840aad49e
commit
3e18434595
@ -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
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue