67 lines
1.8 KiB
Go
67 lines
1.8 KiB
Go
package cdc
|
|
|
|
import (
|
|
"log/slog"
|
|
"sync"
|
|
)
|
|
|
|
// Broadcaster is the in-process fan-out the poller feeds. Subscribers such as
|
|
// terminal session-state fan-out register a callback; every polled Event is
|
|
// delivered to all current subscribers. It is the single seam between the CDC
|
|
// poller and live delivery, so transports can be built and swapped without
|
|
// touching the poller.
|
|
type Broadcaster struct {
|
|
mu sync.RWMutex
|
|
nextID int
|
|
subs map[int]func(Event)
|
|
logger *slog.Logger
|
|
}
|
|
|
|
// NewBroadcaster returns an empty Broadcaster ready for subscriptions.
|
|
func NewBroadcaster() *Broadcaster {
|
|
return &Broadcaster{subs: map[int]func(Event){}, logger: slog.Default()}
|
|
}
|
|
|
|
// Subscribe registers fn and returns an unsubscribe function. fn is called
|
|
// synchronously from the poller loop, so it must not block; a transport that
|
|
// needs buffering should push onto its own channel inside fn.
|
|
func (b *Broadcaster) Subscribe(fn func(Event)) (unsubscribe func()) {
|
|
b.mu.Lock()
|
|
id := b.nextID
|
|
b.nextID++
|
|
b.subs[id] = fn
|
|
b.mu.Unlock()
|
|
return func() {
|
|
b.mu.Lock()
|
|
delete(b.subs, id)
|
|
b.mu.Unlock()
|
|
}
|
|
}
|
|
|
|
// SubscriberCount reports the number of current subscribers.
|
|
func (b *Broadcaster) SubscriberCount() int {
|
|
b.mu.RLock()
|
|
defer b.mu.RUnlock()
|
|
return len(b.subs)
|
|
}
|
|
|
|
// Publish delivers e to every current subscriber. A panicking subscriber is
|
|
// recovered and logged so one bad callback can't kill the poller goroutine or
|
|
// starve the other subscribers.
|
|
func (b *Broadcaster) Publish(e Event) {
|
|
b.mu.RLock()
|
|
defer b.mu.RUnlock()
|
|
for _, fn := range b.subs {
|
|
b.deliver(fn, e)
|
|
}
|
|
}
|
|
|
|
func (b *Broadcaster) deliver(fn func(Event), e Event) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
b.logger.Error("cdc broadcaster: subscriber panicked", "seq", e.Seq, "panic", r)
|
|
}
|
|
}()
|
|
fn(e)
|
|
}
|