agent/grpc: use a separate channel for closing the Accept

Closing l.conns can lead to a race and a 'panic: send on closed chan' when a
connection is in the middle of being handled when the server is shutting down.

Found using '-race -count=800'
This commit is contained in:
Daniel Nephin 2020-10-13 19:21:02 -04:00
parent e640d47319
commit 7e338693a8

View file

@ -22,7 +22,7 @@ func NewHandler(addr net.Addr, register func(server *grpc.Server)) *Handler {
) )
register(srv) register(srv)
lis := &chanListener{addr: addr, conns: make(chan net.Conn)} lis := &chanListener{addr: addr, conns: make(chan net.Conn), done: make(chan struct{})}
return &Handler{srv: srv, listener: lis} return &Handler{srv: srv, listener: lis}
} }
@ -51,22 +51,22 @@ func (h *Handler) Shutdown() error {
type chanListener struct { type chanListener struct {
conns chan net.Conn conns chan net.Conn
addr net.Addr addr net.Addr
done chan struct{}
} }
// Accept blocks until a connection is received from Handle, and then returns the // Accept blocks until a connection is received from Handle, and then returns the
// connection. Accept implements part of the net.Listener interface for grpc.Server. // connection. Accept implements part of the net.Listener interface for grpc.Server.
func (l *chanListener) Accept() (net.Conn, error) { func (l *chanListener) Accept() (net.Conn, error) {
select { select {
case c, ok := <-l.conns: case c := <-l.conns:
if !ok {
return nil, &net.OpError{
Op: "accept",
Net: l.addr.Network(),
Addr: l.addr,
Err: fmt.Errorf("listener closed"),
}
}
return c, nil return c, nil
case <-l.done:
return nil, &net.OpError{
Op: "accept",
Net: l.addr.Network(),
Addr: l.addr,
Err: fmt.Errorf("listener closed"),
}
} }
} }
@ -75,7 +75,7 @@ func (l *chanListener) Addr() net.Addr {
} }
func (l *chanListener) Close() error { func (l *chanListener) Close() error {
close(l.conns) close(l.done)
return nil return nil
} }