Skip to content

Commit 199f9d1

Browse files
authored
Merge pull request #6600 from bhandras/invoiceregistry-deadlock
invoices: fix deadlock in the invoice registry
2 parents addbbc0 + 699f9c0 commit 199f9d1

File tree

2 files changed

+33
-16
lines changed

2 files changed

+33
-16
lines changed

docs/release-notes/release-notes-0.15.0.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,10 @@ compact filters and block/block headers.
121121

122122
* [Fixed an issue where lnd would end up sending an Error and triggering a force
123123
close.](https://github.com/lightningnetwork/lnd/pull/6518)
124-
124+
125+
* [Fixed deadlock in the invoice registry](
126+
https://github.com/lightningnetwork/lnd/pull/6600)
127+
125128
## Neutrino
126129

127130
* [New neutrino sub-server](https://github.com/lightningnetwork/lnd/pull/5652)

invoices/invoiceregistry.go

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ type InvoiceRegistry struct {
108108
// cfg contains the registry's configuration parameters.
109109
cfg *RegistryConfig
110110

111+
// notificationClientMux locks notificationClients and
112+
// singleNotificationClients. Using a separate mutex for these maps is
113+
// necessary to avoid deadlocks in the registry when processing invoice
114+
// events.
115+
notificationClientMux sync.RWMutex
116+
111117
notificationClients map[uint32]*InvoiceSubscription
112118

113119
// TODO(yy): use map[lntypes.Hash]*SingleInvoiceSubscription for better
@@ -118,6 +124,11 @@ type InvoiceRegistry struct {
118124
// carried.
119125
invoiceEvents chan *invoiceEvent
120126

127+
// hodlSubscriptionsMux locks the hodlSubscriptions and
128+
// hodlReverseSubscriptions. Using a separate mutex for these maps is
129+
// necessary to avoid deadlocks in the registry when processing invoice
130+
// events.
131+
hodlSubscriptionsMux sync.RWMutex
121132
// subscriptions is a map from a circuit key to a list of subscribers.
122133
// It is used for efficient notification of links.
123134
hodlSubscriptions map[channeldb.CircuitKey]map[chan<- interface{}]struct{}
@@ -635,9 +646,6 @@ func (i *InvoiceRegistry) startHtlcTimer(invoiceRef channeldb.InvoiceRef,
635646
func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef channeldb.InvoiceRef,
636647
key channeldb.CircuitKey, result FailResolutionResult) error {
637648

638-
i.Lock()
639-
defer i.Unlock()
640-
641649
updateInvoice := func(invoice *channeldb.Invoice) (
642650
*channeldb.InvoiceUpdateDesc, error) {
643651

@@ -1584,9 +1592,9 @@ func (i *InvoiceRegistry) SubscribeNotifications(
15841592
}
15851593
}()
15861594

1587-
i.Lock()
1595+
i.notificationClientMux.Lock()
15881596
i.notificationClients[client.id] = client
1589-
i.Unlock()
1597+
i.notificationClientMux.Unlock()
15901598

15911599
// Query the database to see if based on the provided addIndex and
15921600
// settledIndex we need to deliver any backlog notifications.
@@ -1660,9 +1668,9 @@ func (i *InvoiceRegistry) SubscribeSingleInvoice(
16601668
}
16611669
}()
16621670

1663-
i.Lock()
1671+
i.notificationClientMux.Lock()
16641672
i.singleNotificationClients[client.id] = client
1665-
i.Unlock()
1673+
i.notificationClientMux.Unlock()
16661674

16671675
err := i.deliverSingleBacklogEvents(client)
16681676
if err != nil {
@@ -1678,6 +1686,9 @@ func (i *InvoiceRegistry) SubscribeSingleInvoice(
16781686
// notifyHodlSubscribers sends out the htlc resolution to all current
16791687
// subscribers.
16801688
func (i *InvoiceRegistry) notifyHodlSubscribers(htlcResolution HtlcResolution) {
1689+
i.hodlSubscriptionsMux.Lock()
1690+
defer i.hodlSubscriptionsMux.Unlock()
1691+
16811692
subscribers, ok := i.hodlSubscriptions[htlcResolution.CircuitKey()]
16821693
if !ok {
16831694
return
@@ -1706,6 +1717,9 @@ func (i *InvoiceRegistry) notifyHodlSubscribers(htlcResolution HtlcResolution) {
17061717
func (i *InvoiceRegistry) hodlSubscribe(subscriber chan<- interface{},
17071718
circuitKey channeldb.CircuitKey) {
17081719

1720+
i.hodlSubscriptionsMux.Lock()
1721+
defer i.hodlSubscriptionsMux.Unlock()
1722+
17091723
log.Debugf("Hodl subscribe for %v", circuitKey)
17101724

17111725
subscriptions, ok := i.hodlSubscriptions[circuitKey]
@@ -1725,8 +1739,8 @@ func (i *InvoiceRegistry) hodlSubscribe(subscriber chan<- interface{},
17251739

17261740
// HodlUnsubscribeAll cancels the subscription.
17271741
func (i *InvoiceRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {
1728-
i.Lock()
1729-
defer i.Unlock()
1742+
i.hodlSubscriptionsMux.Lock()
1743+
defer i.hodlSubscriptionsMux.Unlock()
17301744

17311745
hashes := i.hodlReverseSubscriptions[subscriber]
17321746
for hash := range hashes {
@@ -1739,8 +1753,8 @@ func (i *InvoiceRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {
17391753
// copySingleClients copies i.SingleInvoiceSubscription inside a lock. This is
17401754
// useful when we need to iterate the map to send notifications.
17411755
func (i *InvoiceRegistry) copySingleClients() map[uint32]*SingleInvoiceSubscription {
1742-
i.RLock()
1743-
defer i.RUnlock()
1756+
i.notificationClientMux.RLock()
1757+
defer i.notificationClientMux.RUnlock()
17441758

17451759
clients := make(map[uint32]*SingleInvoiceSubscription)
17461760
for k, v := range i.singleNotificationClients {
@@ -1752,8 +1766,8 @@ func (i *InvoiceRegistry) copySingleClients() map[uint32]*SingleInvoiceSubscript
17521766
// copyClients copies i.notificationClients inside a lock. This is useful when
17531767
// we need to iterate the map to send notifications.
17541768
func (i *InvoiceRegistry) copyClients() map[uint32]*InvoiceSubscription {
1755-
i.RLock()
1756-
defer i.RUnlock()
1769+
i.notificationClientMux.RLock()
1770+
defer i.notificationClientMux.RUnlock()
17571771

17581772
clients := make(map[uint32]*InvoiceSubscription)
17591773
for k, v := range i.notificationClients {
@@ -1765,8 +1779,8 @@ func (i *InvoiceRegistry) copyClients() map[uint32]*InvoiceSubscription {
17651779
// deleteClient removes a client by its ID inside a lock. Noop if the client is
17661780
// not found.
17671781
func (i *InvoiceRegistry) deleteClient(clientID uint32) {
1768-
i.Lock()
1769-
defer i.Unlock()
1782+
i.notificationClientMux.Lock()
1783+
defer i.notificationClientMux.Unlock()
17701784

17711785
log.Infof("Cancelling invoice subscription for client=%v", clientID)
17721786
delete(i.notificationClients, clientID)

0 commit comments

Comments
 (0)