channeldb: acquire mutexes in the same order throughout

In this commit, we ensure that the channeldb cacheMu mutex is only ever
aquired _before_ acquiring the main DB lock in the cases where the two
locks need to be held simultaneously.

With this commit, the deadlock demonstrated in the previous commit is
now fixed.
This commit is contained in:
Elle Mouton 2024-01-18 19:33:13 +02:00
parent 76748bdbf7
commit ab06665fde
No known key found for this signature in database
GPG key ID: D7D916376026F177

View file

@ -176,6 +176,9 @@ const (
type ChannelGraph struct {
db kvdb.Backend
// cacheMu guards all caches (rejectCache, chanCache, graphCache). If
// this mutex will be acquired at the same time as the DB mutex then
// the cacheMu MUST be acquired first to prevent deadlock.
cacheMu sync.RWMutex
rejectCache *rejectCache
chanCache *channelCache
@ -2091,6 +2094,9 @@ func (c *ChannelGraph) FilterKnownChanIDs(chansInfo []ChannelUpdateInfo,
var newChanIDs []uint64
c.cacheMu.Lock()
defer c.cacheMu.Unlock()
err := kvdb.Update(c.db, func(tx kvdb.RwTx) error {
edges := tx.ReadBucket(edgeBucket)
if edges == nil {
@ -2143,7 +2149,7 @@ func (c *ChannelGraph) FilterKnownChanIDs(chansInfo []ChannelUpdateInfo,
// and we let it be added to the set of IDs to
// query our peer for.
case isZombie && !isStillZombie:
err := c.markEdgeLive(tx, scid)
err := c.markEdgeLiveUnsafe(tx, scid)
if err != nil {
return err
}
@ -3612,16 +3618,19 @@ func markEdgeZombie(zombieIndex kvdb.RwBucket, chanID uint64, pubKey1,
// MarkEdgeLive clears an edge from our zombie index, deeming it as live.
func (c *ChannelGraph) MarkEdgeLive(chanID uint64) error {
return c.markEdgeLive(nil, chanID)
}
// markEdgeLive clears an edge from the zombie index. This method can be called
// with an existing kvdb.RwTx or the argument can be set to nil in which case a
// new transaction will be created.
func (c *ChannelGraph) markEdgeLive(tx kvdb.RwTx, chanID uint64) error {
c.cacheMu.Lock()
defer c.cacheMu.Unlock()
return c.markEdgeLiveUnsafe(nil, chanID)
}
// markEdgeLiveUnsafe clears an edge from the zombie index. This method can be
// called with an existing kvdb.RwTx or the argument can be set to nil in which
// case a new transaction will be created.
//
// NOTE: this method MUST only be called if the cacheMu has already been
// acquired.
func (c *ChannelGraph) markEdgeLiveUnsafe(tx kvdb.RwTx, chanID uint64) error {
dbFn := func(tx kvdb.RwTx) error {
edges := tx.ReadWriteBucket(edgeBucket)
if edges == nil {