diff --git a/src/sync.h b/src/sync.h --- a/src/sync.h +++ b/src/sync.h @@ -60,6 +60,7 @@ void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, void *cs); void DeleteLock(void *cs); +bool LockStackEmpty(); /** * Call abort() if a potential lock order deadlock bug is detected, instead of @@ -68,21 +69,22 @@ */ extern bool g_debug_lockorder_abort; #else -static inline void EnterCritical(const char *pszName, const char *pszFile, - int nLine, void *cs, bool fTry = false) {} -static inline void LeaveCritical() {} -static inline void CheckLastCritical(void *cs, std::string &lockname, - const char *guardname, const char *file, - int line) {} +inline void EnterCritical(const char *pszName, const char *pszFile, int nLine, + void *cs, bool fTry = false) {} +inline void LeaveCritical() {} +inline void CheckLastCritical(void *cs, std::string &lockname, + const char *guardname, const char *file, + int line) {} template -static inline void AssertLockHeldInternal(const char *pszName, - const char *pszFile, int nLine, - MutexType *cs) +inline void AssertLockHeldInternal(const char *pszName, const char *pszFile, + int nLine, MutexType *cs) ASSERT_EXCLUSIVE_LOCK(cs) {} -static inline void AssertLockNotHeldInternal(const char *pszName, - const char *pszFile, int nLine, - void *cs) {} -static inline void DeleteLock(void *cs) {} +inline void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, + int nLine, void *cs) {} +inline void DeleteLock(void *cs) {} +inline bool LockStackEmpty() { + return true; +} #endif #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) #define AssertLockNotHeld(cs) \ diff --git a/src/sync.cpp b/src/sync.cpp --- a/src/sync.cpp +++ b/src/sync.cpp @@ -142,14 +142,18 @@ if (lockdata.lockorders.count(p1)) { continue; } - lockdata.lockorders.emplace(p1, lock_stack); const LockPair p2 = std::make_pair(c, i.first); - lockdata.invlockorders.insert(p2); if (lockdata.lockorders.count(p2)) { + auto lock_stack_copy = lock_stack; + lock_stack.pop_back(); potential_deadlock_detected(p1, lockdata.lockorders[p2], - lockdata.lockorders[p1]); + lock_stack_copy); + // potential_deadlock_detected() does not return. } + + lockdata.lockorders.emplace(p1, lock_stack); + lockdata.invlockorders.insert(p2); } } @@ -269,6 +273,16 @@ } } +bool LockStackEmpty() { + LockData &lockdata = GetLockData(); + std::lock_guard lock(lockdata.dd_mutex); + const auto it = lockdata.m_lock_stacks.find(std::this_thread::get_id()); + if (it == lockdata.m_lock_stacks.end()) { + return true; + } + return it->second.empty(); +} + bool g_debug_lockorder_abort = true; #endif /* DEBUG_LOCKORDER */ diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp --- a/src/test/sync_tests.cpp +++ b/src/test/sync_tests.cpp @@ -11,6 +11,7 @@ template void TestPotentialDeadLockDetected(MutexType &mutex1, MutexType &mutex2) { { LOCK2(mutex1, mutex2); } + BOOST_CHECK(LockStackEmpty()); bool error_thrown = false; try { LOCK2(mutex2, mutex1); @@ -20,6 +21,7 @@ "potential deadlock detected: mutex1 -> mutex2 -> mutex1"); error_thrown = true; } + BOOST_CHECK(LockStackEmpty()); #ifdef DEBUG_LOCKORDER BOOST_CHECK(error_thrown); #else @@ -38,9 +40,15 @@ RecursiveMutex rmutex1, rmutex2; TestPotentialDeadLockDetected(rmutex1, rmutex2); + // The second test ensures that lock tracking data have not been broken by + // exception. + TestPotentialDeadLockDetected(rmutex1, rmutex2); Mutex mutex1, mutex2; TestPotentialDeadLockDetected(mutex1, mutex2); + // The second test ensures that lock tracking data have not been broken by + // exception. + TestPotentialDeadLockDetected(mutex1, mutex2); #ifdef DEBUG_LOCKORDER g_debug_lockorder_abort = prev;