@@ -108,6 +108,12 @@ type InvoiceRegistry struct {
108
108
// cfg contains the registry's configuration parameters.
109
109
cfg * RegistryConfig
110
110
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
+
111
117
notificationClients map [uint32 ]* InvoiceSubscription
112
118
113
119
// TODO(yy): use map[lntypes.Hash]*SingleInvoiceSubscription for better
@@ -118,6 +124,11 @@ type InvoiceRegistry struct {
118
124
// carried.
119
125
invoiceEvents chan * invoiceEvent
120
126
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
121
132
// subscriptions is a map from a circuit key to a list of subscribers.
122
133
// It is used for efficient notification of links.
123
134
hodlSubscriptions map [channeldb.CircuitKey ]map [chan <- interface {}]struct {}
@@ -635,9 +646,6 @@ func (i *InvoiceRegistry) startHtlcTimer(invoiceRef channeldb.InvoiceRef,
635
646
func (i * InvoiceRegistry ) cancelSingleHtlc (invoiceRef channeldb.InvoiceRef ,
636
647
key channeldb.CircuitKey , result FailResolutionResult ) error {
637
648
638
- i .Lock ()
639
- defer i .Unlock ()
640
-
641
649
updateInvoice := func (invoice * channeldb.Invoice ) (
642
650
* channeldb.InvoiceUpdateDesc , error ) {
643
651
@@ -1584,9 +1592,9 @@ func (i *InvoiceRegistry) SubscribeNotifications(
1584
1592
}
1585
1593
}()
1586
1594
1587
- i .Lock ()
1595
+ i .notificationClientMux . Lock ()
1588
1596
i .notificationClients [client .id ] = client
1589
- i .Unlock ()
1597
+ i .notificationClientMux . Unlock ()
1590
1598
1591
1599
// Query the database to see if based on the provided addIndex and
1592
1600
// settledIndex we need to deliver any backlog notifications.
@@ -1660,9 +1668,9 @@ func (i *InvoiceRegistry) SubscribeSingleInvoice(
1660
1668
}
1661
1669
}()
1662
1670
1663
- i .Lock ()
1671
+ i .notificationClientMux . Lock ()
1664
1672
i .singleNotificationClients [client .id ] = client
1665
- i .Unlock ()
1673
+ i .notificationClientMux . Unlock ()
1666
1674
1667
1675
err := i .deliverSingleBacklogEvents (client )
1668
1676
if err != nil {
@@ -1678,6 +1686,9 @@ func (i *InvoiceRegistry) SubscribeSingleInvoice(
1678
1686
// notifyHodlSubscribers sends out the htlc resolution to all current
1679
1687
// subscribers.
1680
1688
func (i * InvoiceRegistry ) notifyHodlSubscribers (htlcResolution HtlcResolution ) {
1689
+ i .hodlSubscriptionsMux .Lock ()
1690
+ defer i .hodlSubscriptionsMux .Unlock ()
1691
+
1681
1692
subscribers , ok := i .hodlSubscriptions [htlcResolution .CircuitKey ()]
1682
1693
if ! ok {
1683
1694
return
@@ -1706,6 +1717,9 @@ func (i *InvoiceRegistry) notifyHodlSubscribers(htlcResolution HtlcResolution) {
1706
1717
func (i * InvoiceRegistry ) hodlSubscribe (subscriber chan <- interface {},
1707
1718
circuitKey channeldb.CircuitKey ) {
1708
1719
1720
+ i .hodlSubscriptionsMux .Lock ()
1721
+ defer i .hodlSubscriptionsMux .Unlock ()
1722
+
1709
1723
log .Debugf ("Hodl subscribe for %v" , circuitKey )
1710
1724
1711
1725
subscriptions , ok := i .hodlSubscriptions [circuitKey ]
@@ -1725,8 +1739,8 @@ func (i *InvoiceRegistry) hodlSubscribe(subscriber chan<- interface{},
1725
1739
1726
1740
// HodlUnsubscribeAll cancels the subscription.
1727
1741
func (i * InvoiceRegistry ) HodlUnsubscribeAll (subscriber chan <- interface {}) {
1728
- i .Lock ()
1729
- defer i .Unlock ()
1742
+ i .hodlSubscriptionsMux . Lock ()
1743
+ defer i .hodlSubscriptionsMux . Unlock ()
1730
1744
1731
1745
hashes := i .hodlReverseSubscriptions [subscriber ]
1732
1746
for hash := range hashes {
@@ -1739,8 +1753,8 @@ func (i *InvoiceRegistry) HodlUnsubscribeAll(subscriber chan<- interface{}) {
1739
1753
// copySingleClients copies i.SingleInvoiceSubscription inside a lock. This is
1740
1754
// useful when we need to iterate the map to send notifications.
1741
1755
func (i * InvoiceRegistry ) copySingleClients () map [uint32 ]* SingleInvoiceSubscription {
1742
- i .RLock ()
1743
- defer i .RUnlock ()
1756
+ i .notificationClientMux . RLock ()
1757
+ defer i .notificationClientMux . RUnlock ()
1744
1758
1745
1759
clients := make (map [uint32 ]* SingleInvoiceSubscription )
1746
1760
for k , v := range i .singleNotificationClients {
@@ -1752,8 +1766,8 @@ func (i *InvoiceRegistry) copySingleClients() map[uint32]*SingleInvoiceSubscript
1752
1766
// copyClients copies i.notificationClients inside a lock. This is useful when
1753
1767
// we need to iterate the map to send notifications.
1754
1768
func (i * InvoiceRegistry ) copyClients () map [uint32 ]* InvoiceSubscription {
1755
- i .RLock ()
1756
- defer i .RUnlock ()
1769
+ i .notificationClientMux . RLock ()
1770
+ defer i .notificationClientMux . RUnlock ()
1757
1771
1758
1772
clients := make (map [uint32 ]* InvoiceSubscription )
1759
1773
for k , v := range i .notificationClients {
@@ -1765,8 +1779,8 @@ func (i *InvoiceRegistry) copyClients() map[uint32]*InvoiceSubscription {
1765
1779
// deleteClient removes a client by its ID inside a lock. Noop if the client is
1766
1780
// not found.
1767
1781
func (i * InvoiceRegistry ) deleteClient (clientID uint32 ) {
1768
- i .Lock ()
1769
- defer i .Unlock ()
1782
+ i .notificationClientMux . Lock ()
1783
+ defer i .notificationClientMux . Unlock ()
1770
1784
1771
1785
log .Infof ("Cancelling invoice subscription for client=%v" , clientID )
1772
1786
delete (i .notificationClients , clientID )
0 commit comments