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>main
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