@@ -4091,7 +4091,7 @@ func (s *server) InboundPeerConnected(conn net.Conn) {
4091
4091
// Remove the current peer from the server's internal state and
4092
4092
// signal that the peer termination watcher does not need to
4093
4093
// execute for this peer.
4094
- s .removePeer (connectedPeer )
4094
+ s .removePeerUnsafe (connectedPeer )
4095
4095
s .ignorePeerTermination [connectedPeer ] = struct {}{}
4096
4096
s .scheduledPeerConnection [pubStr ] = func () {
4097
4097
s .peerConnected (conn , nil , true )
@@ -4208,7 +4208,7 @@ func (s *server) OutboundPeerConnected(connReq *connmgr.ConnReq, conn net.Conn)
4208
4208
// Remove the current peer from the server's internal state and
4209
4209
// signal that the peer termination watcher does not need to
4210
4210
// execute for this peer.
4211
- s .removePeer (connectedPeer )
4211
+ s .removePeerUnsafe (connectedPeer )
4212
4212
s .ignorePeerTermination [connectedPeer ] = struct {}{}
4213
4213
s .scheduledPeerConnection [pubStr ] = func () {
4214
4214
s .peerConnected (conn , connReq , false )
@@ -4730,7 +4730,7 @@ func (s *server) peerTerminationWatcher(p *peer.Brontide, ready chan struct{}) {
4730
4730
4731
4731
// First, cleanup any remaining state the server has regarding the peer
4732
4732
// in question.
4733
- s .removePeer (p )
4733
+ s .removePeerUnsafe (p )
4734
4734
4735
4735
// Next, check to see if this is a persistent peer or not.
4736
4736
if _ , ok := s .persistentPeers [pubStr ]; ! ok {
@@ -4939,29 +4939,24 @@ func (s *server) connectToPersistentPeer(pubKeyStr string) {
4939
4939
}()
4940
4940
}
4941
4941
4942
- // removePeer removes the passed peer from the server's state of all active
4943
- // peers.
4944
- func (s * server ) removePeer (p * peer.Brontide ) {
4942
+ // removePeerUnsafe removes the passed peer from the server's state of all
4943
+ // active peers.
4944
+ //
4945
+ // NOTE: Server mutex must be held when calling this function.
4946
+ func (s * server ) removePeerUnsafe (p * peer.Brontide ) {
4945
4947
if p == nil {
4946
4948
return
4947
4949
}
4948
4950
4949
- srvrLog .Debugf ("removing peer %v" , p )
4950
-
4951
- // As the peer is now finished, ensure that the TCP connection is
4952
- // closed and all of its related goroutines have exited.
4953
- p .Disconnect (fmt .Errorf ("server: disconnecting peer %v" , p ))
4954
-
4955
- // If this peer had an active persistent connection request, remove it.
4956
- if p .ConnReq () != nil {
4957
- s .connMgr .Remove (p .ConnReq ().ID ())
4958
- }
4951
+ srvrLog .Debugf ("Removing peer %v" , p )
4959
4952
4960
- // Ignore deleting peers if we're shutting down.
4953
+ // Exit early if we have already been instructed to shutdown, the peers
4954
+ // will be disconnected in the server shutdown process.
4961
4955
if s .Stopped () {
4962
4956
return
4963
4957
}
4964
4958
4959
+ // Capture the peer's public key and string representation.
4965
4960
pKey := p .PubKey ()
4966
4961
pubSer := pKey [:]
4967
4962
pubStr := string (pubSer )
@@ -4974,22 +4969,45 @@ func (s *server) removePeer(p *peer.Brontide) {
4974
4969
delete (s .outboundPeers , pubStr )
4975
4970
}
4976
4971
4977
- // Remove the peer's access permission from the access manager.
4978
- peerPubStr := string (p .IdentityKey ().SerializeCompressed ())
4979
- s .peerAccessMan .removePeerAccess (peerPubStr )
4972
+ // When removing the peer we make sure to disconnect it asynchronously
4973
+ // to avoid blocking the main server goroutine because it is holding the
4974
+ // server's mutex. Disconnecting the peer might block and wait until the
4975
+ // peer has fully started up. This can happen if an inbound and outbound
4976
+ // race condition occurs.
4977
+ s .wg .Add (1 )
4978
+ go func () {
4979
+ defer s .wg .Done ()
4980
4980
4981
- // Copy the peer's error buffer across to the server if it has any items
4982
- // in it so that we can restore peer errors across connections.
4983
- if p .ErrorBuffer ().Total () > 0 {
4984
- s .peerErrors [pubStr ] = p .ErrorBuffer ()
4985
- }
4981
+ p .Disconnect (fmt .Errorf ("server: disconnecting peer %v" , p ))
4986
4982
4987
- // Inform the peer notifier of a peer offline event so that it can be
4988
- // reported to clients listening for peer events.
4989
- var pubKey [33 ]byte
4990
- copy (pubKey [:], pubSer )
4983
+ // If this peer had an active persistent connection request,
4984
+ // remove it.
4985
+ if p .ConnReq () != nil {
4986
+ s .connMgr .Remove (p .ConnReq ().ID ())
4987
+ }
4988
+
4989
+ // Remove the peer's access permission from the access manager.
4990
+ peerPubStr := string (p .IdentityKey ().SerializeCompressed ())
4991
+ s .peerAccessMan .removePeerAccess (peerPubStr )
4992
+
4993
+ // Copy the peer's error buffer across to the server if it has
4994
+ // any items in it so that we can restore peer errors across
4995
+ // connections. We need to look up the error after the peer has
4996
+ // been disconnected because we write the error in the
4997
+ // `Disconnect` method.
4998
+ s .mu .Lock ()
4999
+ if p .ErrorBuffer ().Total () > 0 {
5000
+ s .peerErrors [pubStr ] = p .ErrorBuffer ()
5001
+ }
5002
+ s .mu .Unlock ()
5003
+
5004
+ // Inform the peer notifier of a peer offline event so that it
5005
+ // can be reported to clients listening for peer events.
5006
+ var pubKey [33 ]byte
5007
+ copy (pubKey [:], pubSer )
4991
5008
4992
- s .peerNotifier .NotifyPeerOffline (pubKey )
5009
+ s .peerNotifier .NotifyPeerOffline (pubKey )
5010
+ }()
4993
5011
}
4994
5012
4995
5013
// ConnectToPeer requests that the server connect to a Lightning Network peer
@@ -5129,8 +5147,11 @@ func (s *server) DisconnectPeer(pubKey *btcec.PublicKey) error {
5129
5147
delete (s .persistentPeersBackoff , pubStr )
5130
5148
5131
5149
// Remove the peer by calling Disconnect. Previously this was done with
5132
- // removePeer, which bypassed the peerTerminationWatcher.
5133
- peer .Disconnect (fmt .Errorf ("server: DisconnectPeer called" ))
5150
+ // removePeerUnsafe, which bypassed the peerTerminationWatcher.
5151
+ //
5152
+ // NOTE: We call it in a goroutine to avoid blocking the main server
5153
+ // goroutine because we might hold the server's mutex.
5154
+ go peer .Disconnect (fmt .Errorf ("server: DisconnectPeer called" ))
5134
5155
5135
5156
return nil
5136
5157
}
0 commit comments