From 98632a1ebb458283e722b0373830554a0a46ac76 Mon Sep 17 00:00:00 2001 From: Manish R Jain Date: Tue, 14 Sep 2021 08:25:03 -0700 Subject: [PATCH] Fix(levels): Avoid a deadlock when acquiring read locks in levels (#1744) (cherry picked from commit 560e319d7ceb902172132feb71574477f151298d) --- levels.go | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/levels.go b/levels.go index 637a3565d..1c625d1b3 100644 --- a/levels.go +++ b/levels.go @@ -1119,8 +1119,22 @@ func (s *levelsController) fillTablesL0ToL0(cd *compactDef) bool { cd.nextRange = keyRange{} cd.bot = nil - cd.lockLevels() - defer cd.unlockLevels() + // Because this level and next level are both level 0, we should NOT acquire + // the read lock twice, because it can result in a deadlock. So, we don't + // call compactDef.lockLevels, instead locking the level only once and + // directly here. + // + // As per godocs on RWMutex: + // If a goroutine holds a RWMutex for reading and another goroutine might + // call Lock, no goroutine should expect to be able to acquire a read lock + // until the initial read lock is released. In particular, this prohibits + // recursive read locking. This is to ensure that the lock eventually + // becomes available; a blocked Lock call excludes new readers from + // acquiring the lock. + y.AssertTrue(cd.thisLevel.level == 0) + y.AssertTrue(cd.nextLevel.level == 0) + s.levels[0].RLock() + defer s.levels[0].RUnlock() s.cstatus.Lock() defer s.cstatus.Unlock()