control/controlclient: fix leaked http2 goroutines on shutdown

If a noise dial was happening concurrently with shutdown, the
http2 goroutines could leak.

Updates tailscale/corp#14950
Updates tailscale/corp#14515
Updates tailscale/corp#14139
Updates tailscale/corp#13175

Signed-off-by: Brad Fitzpatrick <brad@danga.com>
pull/9573/head
Brad Fitzpatrick 1 year ago
parent 2c0f0ee759
commit 5b3f5eabb5

@ -177,6 +177,7 @@ type NoiseClient struct {
// mu only protects the following variables. // mu only protects the following variables.
mu sync.Mutex mu sync.Mutex
closed bool
last *noiseConn // or nil last *noiseConn // or nil
nextID int nextID int
connPool map[int]*noiseConn // active connections not yet closed; see noiseConn.Close connPool map[int]*noiseConn // active connections not yet closed; see noiseConn.Close
@ -373,6 +374,7 @@ func (nc *NoiseClient) connClosed(id int) {
// It is a no-op and returns nil if the connection is already closed. // It is a no-op and returns nil if the connection is already closed.
func (nc *NoiseClient) Close() error { func (nc *NoiseClient) Close() error {
nc.mu.Lock() nc.mu.Lock()
nc.closed = true
conns := nc.connPool conns := nc.connPool
nc.connPool = nil nc.connPool = nil
nc.mu.Unlock() nc.mu.Unlock()
@ -472,6 +474,10 @@ func (nc *NoiseClient) dial(ctx context.Context) (*noiseConn, error) {
nc.mu.Lock() nc.mu.Lock()
defer nc.mu.Unlock() defer nc.mu.Unlock()
if nc.closed {
ncc.Close()
return nil, errors.New("noise client closed")
}
mak.Set(&nc.connPool, ncc.id, ncc) mak.Set(&nc.connPool, ncc.id, ncc)
nc.last = ncc nc.last = ncc
return ncc, nil return ncc, nil

Loading…
Cancel
Save