go-ssb-room/internal/broadcasts/endpoints.go

108 lines
2.0 KiB
Go

// SPDX-FileCopyrightText: 2021 The NGI Pointer Secure-Scuttlebutt Team of 2020/2021
//
// SPDX-License-Identifier: MIT
package broadcasts
import (
"io"
"sync"
"github.com/ssbc/go-ssb-room/v2/internal/maybemod/multierror"
)
type EndpointsEmitter interface {
Update(members []string) error
io.Closer
}
// NewEndpointsEmitter returns the Sink, to write to the broadcaster, and the new
// broadcast instance.
func NewEndpointsEmitter() (EndpointsEmitter, *EndpointsBroadcast) {
bcst := EndpointsBroadcast{
mu: &sync.Mutex{},
sinks: make(map[*EndpointsEmitter]struct{}),
}
return (*endpointsSink)(&bcst), &bcst
}
// EndpointsBroadcast is an interface for registering one or more Sinks to recieve
// updates.
type EndpointsBroadcast struct {
mu *sync.Mutex
sinks map[*EndpointsEmitter]struct{}
}
// Register a Sink for updates to be sent. also returns
func (bcst *EndpointsBroadcast) Register(sink EndpointsEmitter) func() {
bcst.mu.Lock()
defer bcst.mu.Unlock()
bcst.sinks[&sink] = struct{}{}
return func() {
bcst.mu.Lock()
defer bcst.mu.Unlock()
delete(bcst.sinks, &sink)
sink.Close()
}
}
type endpointsSink EndpointsBroadcast
// Pour implements the Sink interface.
func (bcst *endpointsSink) Update(members []string) error {
bcst.mu.Lock()
for s := range bcst.sinks {
err := (*s).Update(members)
if err != nil {
delete(bcst.sinks, s)
}
}
bcst.mu.Unlock()
return nil
}
// Close implements the Sink interface.
func (bcst *endpointsSink) Close() error {
var sinks []EndpointsEmitter
bcst.mu.Lock()
defer bcst.mu.Unlock()
sinks = make([]EndpointsEmitter, 0, len(bcst.sinks))
for sink := range bcst.sinks {
sinks = append(sinks, *sink)
}
var (
wg sync.WaitGroup
me multierror.List
)
// might be fine without the waitgroup and concurrency
wg.Add(len(sinks))
for _, sink_ := range sinks {
go func(sink EndpointsEmitter) {
defer wg.Done()
err := sink.Close()
if err != nil {
me.Errs = append(me.Errs, err)
return
}
}(sink_)
}
wg.Wait()
if len(me.Errs) == 0 {
return nil
}
return me
}