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

108 lines
2.0 KiB
Go
Raw Normal View History

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