pomerium/pkg/fanout/subscribers.go
2023-12-11 17:31:39 -05:00

65 lines
1.7 KiB
Go

package fanout
import (
"context"
)
// subscriber represents an individual subscriber to a fanout
type subscriber[T any] struct {
// messages is the channel that the subscriber receives messages on
messages chan<- T
// done is closed when the subscriber is closed
// it is used to signal the dispatchLoop that the subscriber is closed and should be removed
done <-chan struct{}
// cancel is passed so that dispatchLoop can cancel the subscriber if it discards it from its side
cancel context.CancelCauseFunc
// filter identifies the messages that the subscriber is interested in.
filter func(T) bool
// onAdded is called when the subscriber is added to the fanout
// it is only used for tests
onAdded func()
}
// subscriberCloseFn is a function that closes a subscriber and propagates an error to it
type subscriberCloseFn func(err error)
// subscribers is a map of subscribers to their close functions
type subscribers[T any] map[*subscriber[T]]subscriberCloseFn
// closeAll closes all subscribers and propagates the given error to them
func (s subscribers[T]) closeAll(err error) {
for _, close := range s {
close(err)
}
}
// add adds subscriber to the fanout
func (s subscribers[T]) add(sub *subscriber[T]) {
s[sub] = func(err error) {
close(sub.messages)
sub.cancel(err)
delete(s, sub)
}
if sub.onAdded != nil {
sub.onAdded()
}
}
// dispatch dispatches the given message to all subscribers
func (s subscribers[T]) dispatch(ctx context.Context, msg T) {
for sub, close := range s {
if sub.filter != nil && !sub.filter(msg) {
continue
}
select {
case <-ctx.Done():
return
case sub.messages <- msg:
case <-sub.done:
close(ErrSubscriberClosed)
default:
close(ErrSubscriberEvicted)
}
}
}