From 407184ac51a34cedaf6dd793a2a045c6bf03f47c Mon Sep 17 00:00:00 2001 From: eugene Date: Tue, 14 Jun 2022 13:58:43 -0400 Subject: [PATCH 1/2] htlcswitch: add linkStopIndex to cleanly shutdown ChannelLink With this, extra calls to RemoveLink will wait for the link to fully stop. This is accomplished by a map that stores a single stop channel that callers to RemoveLink will listen on. This map is not consulted when the Switch is shutting down and calls Stop on each individual link. Though that could be added in the future, it is not necessary. --- htlcswitch/switch.go | 54 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index b6e3e2537..72d6f5968 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -267,6 +267,15 @@ type Switch struct { // channels that the switch maintains with that peer. interfaceIndex map[[33]byte]map[lnwire.ChannelID]ChannelLink + // linkStopIndex stores the currently stopping ChannelLinks, + // represented by their ChannelID. The key is the link's ChannelID and + // the value is a chan that is closed when the link has fully stopped. + // This map is only added to if RemoveLink is called and is not added + // to when the Switch is shutting down and calls Stop() on each link. + // + // MUST be used with the indexMtx. + linkStopIndex map[lnwire.ChannelID]chan struct{} + // htlcPlex is the channel which all connected links use to coordinate // the setup/teardown of Sphinx (onion routing) payment circuits. // Active links forward any add/settle messages over this channel each @@ -327,6 +336,7 @@ func New(cfg Config, currentHeight uint32) (*Switch, error) { forwardingIndex: make(map[lnwire.ShortChannelID]ChannelLink), interfaceIndex: make(map[[33]byte]map[lnwire.ChannelID]ChannelLink), pendingLinkIndex: make(map[lnwire.ChannelID]ChannelLink), + linkStopIndex: make(map[lnwire.ChannelID]chan struct{}), networkResults: newNetworkResultStore(cfg.DB), htlcPlex: make(chan *plexPacket), chanCloseRequests: make(chan *ChanClose), @@ -2269,12 +2279,50 @@ func (s *Switch) HasActiveLink(chanID lnwire.ChannelID) bool { // returns after the link has been completely shutdown. func (s *Switch) RemoveLink(chanID lnwire.ChannelID) { s.indexMtx.Lock() - link := s.removeLink(chanID) + link, err := s.getLink(chanID) + if err != nil { + // If err is non-nil, this means that link is also nil. The + // link variable cannot be nil without err being non-nil. + s.indexMtx.Unlock() + log.Tracef("Unable to remove link for ChannelID(%v): %v", + chanID, err) + return + } + + // Check if the link is already stopping and grab the stop chan if it + // is. + stopChan, ok := s.linkStopIndex[chanID] + if !ok { + // If the link is non-nil, it is not currently stopping, so + // we'll add a stop chan to the linkStopIndex. + stopChan = make(chan struct{}) + s.linkStopIndex[chanID] = stopChan + } s.indexMtx.Unlock() - if link != nil { - link.Stop() + if ok { + // If the stop chan exists, we will wait for it to be closed. + // Once it is closed, we will exit. + select { + case <-stopChan: + return + case <-s.quit: + return + } } + + // Stop the link before removing it from the maps. + link.Stop() + + s.indexMtx.Lock() + _ = s.removeLink(chanID) + + // Close stopChan and remove this link from the linkStopIndex. + // Deleting from the index and removing from the link must be done + // in the same block while the mutex is held. + close(stopChan) + delete(s.linkStopIndex, chanID) + s.indexMtx.Unlock() } // removeLink is used to remove and stop the channel link. From 0cbf0e62087baf5279e24eb2949b8946bc41346c Mon Sep 17 00:00:00 2001 From: eugene Date: Thu, 30 Jun 2022 15:02:28 -0400 Subject: [PATCH 2/2] release-notes: update for 0.15.1 --- docs/release-notes/release-notes-0.15.1.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/release-notes/release-notes-0.15.1.md b/docs/release-notes/release-notes-0.15.1.md index abe1792db..98e5ac231 100644 --- a/docs/release-notes/release-notes-0.15.1.md +++ b/docs/release-notes/release-notes-0.15.1.md @@ -42,6 +42,9 @@ * [DisconnectPeer no longer interferes with the peerTerminationWatcher. This previously caused force closes.](https://github.com/lightningnetwork/lnd/pull/6655) +* [The HtlcSwitch now waits for a ChannelLink to stop before replacing it. This fixes a race + condition.](https://github.com/lightningnetwork/lnd/pull/6642) + # Contributors (Alphabetical Order) * Elle Mouton