Skip to content

Commit 26b646e

Browse files
committed
quic: avoid deadlock in Endpoint.Close
Don't hold Endpoint.connsMu while calling Conn methods that can indirectly depend on acquiring it. Also change test cleanup to not wait for connections to drain when closing a test Endpoint, removing an unnecessary 0.1s delay in test runtime. Fixes golang/go#64982. Change-Id: If336e63b0a7f5b8d2ef63986d36f9ee38a92c477 Reviewed-on: https://go-review.googlesource.com/c/net/+/554695 Reviewed-by: Jonathan Amsterdam <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent cb5b10f commit 26b646e

File tree

2 files changed

+12
-6
lines changed

2 files changed

+12
-6
lines changed

internal/quic/endpoint.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -103,25 +103,31 @@ func (e *Endpoint) LocalAddr() netip.AddrPort {
103103
// It waits for the peers of any open connection to acknowledge the connection has been closed.
104104
func (e *Endpoint) Close(ctx context.Context) error {
105105
e.acceptQueue.close(errors.New("endpoint closed"))
106+
107+
// It isn't safe to call Conn.Abort or conn.exit with connsMu held,
108+
// so copy the list of conns.
109+
var conns []*Conn
106110
e.connsMu.Lock()
107111
if !e.closing {
108-
e.closing = true
112+
e.closing = true // setting e.closing prevents new conns from being created
109113
for c := range e.conns {
110-
c.Abort(localTransportError{code: errNo})
114+
conns = append(conns, c)
111115
}
112116
if len(e.conns) == 0 {
113117
e.udpConn.Close()
114118
}
115119
}
116120
e.connsMu.Unlock()
121+
122+
for _, c := range conns {
123+
c.Abort(localTransportError{code: errNo})
124+
}
117125
select {
118126
case <-e.closec:
119127
case <-ctx.Done():
120-
e.connsMu.Lock()
121-
for c := range e.conns {
128+
for _, c := range conns {
122129
c.exit()
123130
}
124-
e.connsMu.Unlock()
125131
return ctx.Err()
126132
}
127133
return nil

internal/quic/endpoint_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func newLocalEndpoint(t *testing.T, side connSide, conf *Config) *Endpoint {
9797
t.Fatal(err)
9898
}
9999
t.Cleanup(func() {
100-
e.Close(context.Background())
100+
e.Close(canceledContext())
101101
})
102102
return e
103103
}

0 commit comments

Comments
 (0)