@@ -74,13 +74,13 @@ type Conn struct {
7474
7575 handshakeCompletedSuccessfully atomic.Value
7676 handshakeMutex sync.Mutex
77+ handshakeDone chan struct {}
7778
7879 encryptedPackets []addrPkt
7980
8081 connectionClosedByUser bool
8182 closeLock sync.Mutex
8283 closed * closer.Closer
83- handshakeLoopsFinished sync.WaitGroup
8484
8585 readDeadline * deadline.Deadline
8686 writeDeadline * deadline.Deadline
@@ -256,6 +256,12 @@ func (c *Conn) HandshakeContext(ctx context.Context) error {
256256 return nil
257257 }
258258
259+ handshakeDone := make (chan struct {})
260+ defer close (handshakeDone )
261+ c .closeLock .Lock ()
262+ c .handshakeDone = handshakeDone
263+ c .closeLock .Unlock ()
264+
259265 // rfc5246#section-7.4.3
260266 // In addition, the hash and signature algorithms MUST be compatible
261267 // with the key in the server's end-entity certificate.
@@ -405,7 +411,12 @@ func (c *Conn) Write(p []byte) (int, error) {
405411// Close closes the connection.
406412func (c * Conn ) Close () error {
407413 err := c .close (true ) //nolint:contextcheck
408- c .handshakeLoopsFinished .Wait ()
414+ c .closeLock .Lock ()
415+ handshakeDone := c .handshakeDone
416+ c .closeLock .Unlock ()
417+ if handshakeDone != nil {
418+ <- handshakeDone
419+ }
409420 return err
410421}
411422
@@ -1026,7 +1037,6 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh
10261037
10271038 done := make (chan struct {})
10281039 ctxRead , cancelRead := context .WithCancel (context .Background ())
1029- c .cancelHandshakeReader = cancelRead
10301040 cfg .onFlightState = func (_ flightVal , s handshakeState ) {
10311041 if s == handshakeFinished && ! c .isHandshakeCompletedSuccessfully () {
10321042 c .setHandshakeCompletedSuccessfully ()
@@ -1035,16 +1045,21 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh
10351045 }
10361046
10371047 ctxHs , cancel := context .WithCancel (context .Background ())
1048+
1049+ c .closeLock .Lock ()
10381050 c .cancelHandshaker = cancel
1051+ c .cancelHandshakeReader = cancelRead
1052+ c .closeLock .Unlock ()
10391053
10401054 firstErr := make (chan error , 1 )
10411055
1042- c .handshakeLoopsFinished .Add (2 )
1056+ var handshakeLoopsFinished sync.WaitGroup
1057+ handshakeLoopsFinished .Add (2 )
10431058
10441059 // Handshake routine should be live until close.
10451060 // The other party may request retransmission of the last flight to cope with packet drop.
10461061 go func () {
1047- defer c . handshakeLoopsFinished .Done ()
1062+ defer handshakeLoopsFinished .Done ()
10481063 err := c .fsm .Run (ctxHs , c , initialState )
10491064 if ! errors .Is (err , context .Canceled ) {
10501065 select {
@@ -1064,7 +1079,7 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh
10641079 // Force stop handshaker when the underlying connection is closed.
10651080 cancel ()
10661081 }()
1067- defer c . handshakeLoopsFinished .Done ()
1082+ defer handshakeLoopsFinished .Done ()
10681083 for {
10691084 if err := c .readAndBuffer (ctxRead ); err != nil {
10701085 var e * alertError
@@ -1123,12 +1138,12 @@ func (c *Conn) handshake(ctx context.Context, cfg *handshakeConfig, initialFligh
11231138 case err := <- firstErr :
11241139 cancelRead ()
11251140 cancel ()
1126- c . handshakeLoopsFinished .Wait ()
1141+ handshakeLoopsFinished .Wait ()
11271142 return c .translateHandshakeCtxError (err )
11281143 case <- ctx .Done ():
11291144 cancelRead ()
11301145 cancel ()
1131- c . handshakeLoopsFinished .Wait ()
1146+ handshakeLoopsFinished .Wait ()
11321147 return c .translateHandshakeCtxError (ctx .Err ())
11331148 case <- done :
11341149 return nil
@@ -1146,8 +1161,13 @@ func (c *Conn) translateHandshakeCtxError(err error) error {
11461161}
11471162
11481163func (c * Conn ) close (byUser bool ) error {
1149- c .cancelHandshaker ()
1150- c .cancelHandshakeReader ()
1164+ c .closeLock .Lock ()
1165+ cancelHandshaker := c .cancelHandshaker
1166+ cancelHandshakeReader := c .cancelHandshakeReader
1167+ c .closeLock .Unlock ()
1168+
1169+ cancelHandshaker ()
1170+ cancelHandshakeReader ()
11511171
11521172 if c .isHandshakeCompletedSuccessfully () && byUser {
11531173 // Discard error from notify() to return non-error on the first user call of Close()
0 commit comments