Safety improvements
This commit is contained in:
parent
b3693522ae
commit
d9aaac3b0c
4 changed files with 36 additions and 9 deletions
|
@ -2,6 +2,7 @@ package nexus
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -41,13 +42,17 @@ type ToClient interface {
|
||||||
protoreflect.ProtoMessage
|
protoreflect.ProtoMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrSentToClosed = errors.New("sent to closed connection")
|
||||||
|
)
|
||||||
|
|
||||||
type Connection interface {
|
type Connection interface {
|
||||||
io.Closer
|
io.Closer
|
||||||
CloseCh()
|
CloseCh()
|
||||||
|
|
||||||
Shutdown()
|
Shutdown()
|
||||||
|
|
||||||
Send(ToClient) (closed bool)
|
Send(ToClient) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Nexus) NewClient(conn Connection) Client {
|
func (n *Nexus) NewClient(conn Connection) Client {
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
|
|
||||||
"dynatron.me/x/stillbox/pkg/calls"
|
"dynatron.me/x/stillbox/pkg/calls"
|
||||||
"dynatron.me/x/stillbox/pkg/pb"
|
"dynatron.me/x/stillbox/pkg/pb"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Nexus struct {
|
type Nexus struct {
|
||||||
|
@ -67,9 +69,13 @@ func (n *Nexus) broadcastCallToClients(ctx context.Context, call *calls.Call) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if cl.Send(message) {
|
switch err := cl.Send(message); err {
|
||||||
|
case ErrSentToClosed:
|
||||||
// we already hold the lock, and the channel is closed anyway
|
// we already hold the lock, and the channel is closed anyway
|
||||||
delete(n.clients, cl)
|
delete(n.clients, cl)
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
log.Error().Err(err).Msg("broadcast send failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,6 +96,11 @@ func (n *Nexus) Unregister(c Client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Nexus) Shutdown() {
|
func (n *Nexus) Shutdown() {
|
||||||
|
n.Lock()
|
||||||
|
defer n.Unlock()
|
||||||
|
|
||||||
|
close(n.callCh)
|
||||||
|
|
||||||
for c := range n.clients {
|
for c := range n.clients {
|
||||||
c.Shutdown()
|
c.Shutdown()
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,15 +38,15 @@ type wsConn struct {
|
||||||
out chan ToClient
|
out chan ToClient
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wsConn) Send(msg ToClient) (closed bool) {
|
func (w *wsConn) Send(msg ToClient) error {
|
||||||
select {
|
select {
|
||||||
case w.out <- msg:
|
case w.out <- msg:
|
||||||
default:
|
default:
|
||||||
log.Debug().Str("conn", w.RemoteAddr().String()).Msg("send channel not ready, closing")
|
log.Debug().Str("conn", w.RemoteAddr().String()).Msg("send channel not ready, closing")
|
||||||
return true
|
return ErrSentToClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWsConn(c *websocket.Conn) *wsConn {
|
func newWsConn(c *websocket.Conn) *wsConn {
|
||||||
|
|
|
@ -2,6 +2,8 @@ package sinks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
"dynatron.me/x/stillbox/pkg/calls"
|
"dynatron.me/x/stillbox/pkg/calls"
|
||||||
|
@ -22,10 +24,16 @@ type sinkInstance struct {
|
||||||
Required bool
|
Required bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Sinks []sinkInstance
|
type Sinks struct {
|
||||||
|
sync.RWMutex
|
||||||
|
sinks []sinkInstance
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Sinks) Register(name string, toAdd Sink, required bool) {
|
func (s *Sinks) Register(name string, toAdd Sink, required bool) {
|
||||||
*s = append(*s, sinkInstance{
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
s.sinks = append(s.sinks, sinkInstance{
|
||||||
Name: name,
|
Name: name,
|
||||||
Sink: toAdd,
|
Sink: toAdd,
|
||||||
Required: required,
|
Required: required,
|
||||||
|
@ -33,9 +41,12 @@ func (s *Sinks) Register(name string, toAdd Sink, required bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sinks) EmitCall(ctx context.Context, call *calls.Call) error {
|
func (s *Sinks) EmitCall(ctx context.Context, call *calls.Call) error {
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
g, ctx := errgroup.WithContext(ctx)
|
g, ctx := errgroup.WithContext(ctx)
|
||||||
for i := range *s {
|
for i := range s.sinks {
|
||||||
sink := (*s)[i]
|
sink := s.sinks[i]
|
||||||
g.Go(sink.callEmitter(ctx, call))
|
g.Go(sink.callEmitter(ctx, call))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue