mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-08-30 15:40:59 +02:00
Merge pull request #6600 from bhandras/invoiceregistry-deadlock
invoices: fix deadlock in the invoice registry
This commit is contained in:
@@ -121,7 +121,10 @@ compact filters and block/block headers.
|
|||||||
|
|
||||||
* [Fixed an issue where lnd would end up sending an Error and triggering a force
|
* [Fixed an issue where lnd would end up sending an Error and triggering a force
|
||||||
close.](https://github.com/lightningnetwork/lnd/pull/6518)
|
close.](https://github.com/lightningnetwork/lnd/pull/6518)
|
||||||
|
|
||||||
|
* [Fixed deadlock in the invoice registry](
|
||||||
|
https://github.com/lightningnetwork/lnd/pull/6600)
|
||||||
|
|
||||||
## Neutrino
|
## Neutrino
|
||||||
|
|
||||||
* [New neutrino sub-server](https://github.com/lightningnetwork/lnd/pull/5652)
|
* [New neutrino sub-server](https://github.com/lightningnetwork/lnd/pull/5652)
|
||||||
|
@@ -108,6 +108,12 @@ type InvoiceRegistry struct {
|
|||||||
// cfg contains the registry's configuration parameters.
|
// cfg contains the registry's configuration parameters.
|
||||||
cfg *RegistryConfig
|
cfg *RegistryConfig
|
||||||
|
|
||||||
|
// notificationClientMux locks notificationClients and
|
||||||
|
// singleNotificationClients. Using a separate mutex for these maps is
|
||||||
|
// necessary to avoid deadlocks in the registry when processing invoice
|
||||||
|
// events.
|
||||||
|
notificationClientMux sync.RWMutex
|
||||||
|
|
||||||
notificationClients map[uint32]*InvoiceSubscription
|
notificationClients map[uint32]*InvoiceSubscription
|
||||||
|
|
||||||
// TODO(yy): use map[lntypes.Hash]*SingleInvoiceSubscription for better
|
// TODO(yy): use map[lntypes.Hash]*SingleInvoiceSubscription for better
|
||||||
@@ -118,6 +124,11 @@ type InvoiceRegistry struct {
|
|||||||
// carried.
|
// carried.
|
||||||
invoiceEvents chan *invoiceEvent
|
invoiceEvents chan *invoiceEvent
|
||||||
|
|
||||||
|
// hodlSubscriptionsMux locks the hodlSubscriptions and
|
||||||
|
// hodlReverseSubscriptions. Using a separate mutex for these maps is
|
||||||
|
// necessary to avoid deadlocks in the registry when processing invoice
|
||||||
|
// events.
|
||||||
|
hodlSubscriptionsMux sync.RWMutex
|
||||||
// subscriptions is a map from a circuit key to a list of subscribers.
|
// subscriptions is a map from a circuit key to a list of subscribers.
|
||||||
// It is used for efficient notification of links.
|
// It is used for efficient notification of links.
|
||||||
hodlSubscriptions map[channeldb.CircuitKey]map[chan<- interface{}]struct{}
|
hodlSubscriptions map[channeldb.CircuitKey]map[chan<- interface{}]struct{}
|
||||||
@@ -635,9 +646,6 @@ func (i *InvoiceRegistry) startHtlcTimer(invoiceRef channeldb.InvoiceRef,
|
|||||||
func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef channeldb.InvoiceRef,
|
func (i *InvoiceRegistry) cancelSingleHtlc(invoiceRef channeldb.InvoiceRef,
|
||||||
key channeldb.CircuitKey, result FailResolutionResult) error {
|
key channeldb.CircuitKey, result FailResolutionResult) error {
|
||||||
|
|
||||||
i.Lock()
|
|
||||||
defer i.Unlock()
|
|
||||||
|
|
||||||
updateInvoice := func(invoice *channeldb.Invoice) (
|
updateInvoice := func(invoice *channeldb.Invoice) (
|
||||||
*channeldb.InvoiceUpdateDesc, error) {
|
*channeldb.InvoiceUpdateDesc, error) {
|
||||||
|
|
||||||
@@ -1584,9 +1592,9 @@ func (i *InvoiceRegistry) SubscribeNotifications(
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
i.Lock()
|
i.notificationClientMux.Lock()
|
||||||
i.notificationClients[client.id] = client
|
i.notificationClients[client.id] = client
|
||||||
i.Unlock()
|
i.notificationClientMux.Unlock()
|
||||||
|
|
||||||
// Query the database to see if based on the provided addIndex and
|
// Query the database to see if based on the provided addIndex and
|
||||||
// settledIndex we need to deliver any backlog notifications.
|
// settledIndex we need to deliver any backlog notifications.
|
||||||
@@ -1660,9 +1668,9 @@ func (i *InvoiceRegistry) SubscribeSingleInvoice(
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
i.Lock()
|
i.notificationClientMux.Lock()
|
||||||
i.singleNotificationClients[client.id] = client
|
i.singleNotificationClients[client.id] = client
|
||||||
i.Unlock()
|
i.notificationClientMux.Unlock()
|
||||||
|
|
||||||
err := i.deliverSingleBacklogEvents(client)
|
err := i.deliverSingleBacklogEvents(client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1678,6 +1686,9 @@ func (i *InvoiceRegistry) SubscribeSingleInvoice(
|
|||||||
// notifyHodlSubscribers sends out the htlc resolution to all current
|
// notifyHodlSubscribers sends out the htlc resolution to all current
|
||||||
// subscribers.
|
// subscribers.
|
||||||
func (i *InvoiceRegistry) notifyHodlSubscribers(htlcResolution HtlcResolution) {
|
func (i *InvoiceRegistry) notifyHodlSubscribers(htlcResolution HtlcResolution) {
|
||||||
|
i.hodlSubscriptionsMux.Lock()
|
||||||
|
defer i.hodlSubscriptionsMux.Unlock()
|
||||||
|
|
||||||
subscribers, ok := i.hodlSubscriptions[htlcResolution.CircuitKey()]
|
subscribers, ok := i.hodlSubscriptions[htlcResolution.CircuitKey()]
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
@@ -1706,6 +1717,9 @@ func (i *InvoiceRegistry) notifyHodlSubscribers(htlcResolution HtlcResolution) {
|
|||||||
func (i *InvoiceRegistry) hodlSubscribe(subscriber chan<- interface{},
|
func (i *InvoiceRegistry) hodlSubscribe(subscriber chan<- interface{},
|
||||||
circuitKey channeldb.CircuitKey) {
|
circuitKey channeldb.CircuitKey) {
|
||||||
|
|
||||||
|
i.hodlSubscriptionsMux.Lock()
|
||||||
|
defer i.hodlSubscriptionsMux.Unlock()
|
||||||
|
|
||||||
log.Debugf("Hodl subscribe for %v", circuitKey)
|
log.Debugf("Hodl subscribe for %v", circuitKey)
|
||||||
|
|
||||||
subscriptions, ok := i.hodlSubscriptions[circuitKey]
|
subscriptions, ok := i.hodlSubscriptions[circuitKey]
|
||||||
@@ -1725,8 +1739,8 @@ func (i *InvoiceRegistry) hodlSubscribe(subscriber chan<- interface{},
|
|||||||
|
|
||||||
// HodlUnsubscribeAll cancels the subscription.
|
// HodlUnsubscribeAll cancels the subscription.
|
||||||
func (i *InvoiceRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {
|
func (i *InvoiceRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {
|
||||||
i.Lock()
|
i.hodlSubscriptionsMux.Lock()
|
||||||
defer i.Unlock()
|
defer i.hodlSubscriptionsMux.Unlock()
|
||||||
|
|
||||||
hashes := i.hodlReverseSubscriptions[subscriber]
|
hashes := i.hodlReverseSubscriptions[subscriber]
|
||||||
for hash := range hashes {
|
for hash := range hashes {
|
||||||
@@ -1739,8 +1753,8 @@ func (i *InvoiceRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {
|
|||||||
// copySingleClients copies i.SingleInvoiceSubscription inside a lock. This is
|
// copySingleClients copies i.SingleInvoiceSubscription inside a lock. This is
|
||||||
// useful when we need to iterate the map to send notifications.
|
// useful when we need to iterate the map to send notifications.
|
||||||
func (i *InvoiceRegistry) copySingleClients() map[uint32]*SingleInvoiceSubscription {
|
func (i *InvoiceRegistry) copySingleClients() map[uint32]*SingleInvoiceSubscription {
|
||||||
i.RLock()
|
i.notificationClientMux.RLock()
|
||||||
defer i.RUnlock()
|
defer i.notificationClientMux.RUnlock()
|
||||||
|
|
||||||
clients := make(map[uint32]*SingleInvoiceSubscription)
|
clients := make(map[uint32]*SingleInvoiceSubscription)
|
||||||
for k, v := range i.singleNotificationClients {
|
for k, v := range i.singleNotificationClients {
|
||||||
@@ -1752,8 +1766,8 @@ func (i *InvoiceRegistry) copySingleClients() map[uint32]*SingleInvoiceSubscript
|
|||||||
// copyClients copies i.notificationClients inside a lock. This is useful when
|
// copyClients copies i.notificationClients inside a lock. This is useful when
|
||||||
// we need to iterate the map to send notifications.
|
// we need to iterate the map to send notifications.
|
||||||
func (i *InvoiceRegistry) copyClients() map[uint32]*InvoiceSubscription {
|
func (i *InvoiceRegistry) copyClients() map[uint32]*InvoiceSubscription {
|
||||||
i.RLock()
|
i.notificationClientMux.RLock()
|
||||||
defer i.RUnlock()
|
defer i.notificationClientMux.RUnlock()
|
||||||
|
|
||||||
clients := make(map[uint32]*InvoiceSubscription)
|
clients := make(map[uint32]*InvoiceSubscription)
|
||||||
for k, v := range i.notificationClients {
|
for k, v := range i.notificationClients {
|
||||||
@@ -1765,8 +1779,8 @@ func (i *InvoiceRegistry) copyClients() map[uint32]*InvoiceSubscription {
|
|||||||
// deleteClient removes a client by its ID inside a lock. Noop if the client is
|
// deleteClient removes a client by its ID inside a lock. Noop if the client is
|
||||||
// not found.
|
// not found.
|
||||||
func (i *InvoiceRegistry) deleteClient(clientID uint32) {
|
func (i *InvoiceRegistry) deleteClient(clientID uint32) {
|
||||||
i.Lock()
|
i.notificationClientMux.Lock()
|
||||||
defer i.Unlock()
|
defer i.notificationClientMux.Unlock()
|
||||||
|
|
||||||
log.Infof("Cancelling invoice subscription for client=%v", clientID)
|
log.Infof("Cancelling invoice subscription for client=%v", clientID)
|
||||||
delete(i.notificationClients, clientID)
|
delete(i.notificationClients, clientID)
|
||||||
|
Reference in New Issue
Block a user