Skip to content

Commit 0f16212

Browse files
author
MarcoFalke
committed
Merge #19340: Preserve the LockData initial state if "potential deadlock detected" exception thrown
63e9e40 test: Add LockStackEmpty() (Hennadii Stepanov) 42b2a95 test: Repeat deadlock tests (Hennadii Stepanov) 1f96be2 Preserve initial state if push_lock() throws exception (Hennadii Stepanov) Pull request description: On master (e3fa3c7) if the `push_lock()` throws the "potential deadlock detected" exception (via the `potential_deadlock_detected()` call), the `LockData` instance internal state differs from one when the `push_lock()` was called. This non-well behaviour makes (at least) testing brittle. This PR preserves the `LockData` instance initial state if `push_lock()` throws an exception, and improves the `sync_tests` unit test. ACKs for top commit: MarcoFalke: re-ACK 63e9e40 vasild: ACK 63e9e40 Tree-SHA512: 7679182154ce5f079b44b790faf76eb5f553328dea70a326ff6b600db70e2f9ae015a33a104ca070cb660318280cb79b6b42e37ea5166f26f9e627ba721fcdec
2 parents 3c93623 + 63e9e40 commit 0f16212

File tree

3 files changed

+33
-9
lines changed

3 files changed

+33
-9
lines changed

src/sync.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,12 +149,17 @@ static void push_lock(void* c, const CLockLocation& locklocation)
149149
const LockPair p1 = std::make_pair(i.first, c);
150150
if (lockdata.lockorders.count(p1))
151151
continue;
152-
lockdata.lockorders.emplace(p1, lock_stack);
153152

154153
const LockPair p2 = std::make_pair(c, i.first);
154+
if (lockdata.lockorders.count(p2)) {
155+
auto lock_stack_copy = lock_stack;
156+
lock_stack.pop_back();
157+
potential_deadlock_detected(p1, lockdata.lockorders[p2], lock_stack_copy);
158+
// potential_deadlock_detected() does not return.
159+
}
160+
161+
lockdata.lockorders.emplace(p1, lock_stack);
155162
lockdata.invlockorders.insert(p2);
156-
if (lockdata.lockorders.count(p2))
157-
potential_deadlock_detected(p1, lockdata.lockorders[p2], lockdata.lockorders[p1]);
158163
}
159164
}
160165

@@ -259,6 +264,17 @@ void DeleteLock(void* cs)
259264
}
260265
}
261266

267+
bool LockStackEmpty()
268+
{
269+
LockData& lockdata = GetLockData();
270+
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
271+
const auto it = lockdata.m_lock_stacks.find(std::this_thread::get_id());
272+
if (it == lockdata.m_lock_stacks.end()) {
273+
return true;
274+
}
275+
return it->second.empty();
276+
}
277+
262278
bool g_debug_lockorder_abort = true;
263279

264280
#endif /* DEBUG_LOCKORDER */

src/sync.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ template <typename MutexType>
5656
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs);
5757
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
5858
void DeleteLock(void* cs);
59+
bool LockStackEmpty();
5960

6061
/**
6162
* Call abort() if a potential lock order deadlock bug is detected, instead of
@@ -64,13 +65,14 @@ void DeleteLock(void* cs);
6465
*/
6566
extern bool g_debug_lockorder_abort;
6667
#else
67-
void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
68-
void static inline LeaveCritical() {}
69-
void static inline CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
68+
inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
69+
inline void LeaveCritical() {}
70+
inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
7071
template <typename MutexType>
71-
void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
72-
void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
73-
void static inline DeleteLock(void* cs) {}
72+
inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
73+
inline void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
74+
inline void DeleteLock(void* cs) {}
75+
inline bool LockStackEmpty() { return true; }
7476
#endif
7577
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
7678
#define AssertLockNotHeld(cs) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs)

src/test/sync_tests.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2)
1414
{
1515
LOCK2(mutex1, mutex2);
1616
}
17+
BOOST_CHECK(LockStackEmpty());
1718
bool error_thrown = false;
1819
try {
1920
LOCK2(mutex2, mutex1);
2021
} catch (const std::logic_error& e) {
2122
BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected: mutex1 -> mutex2 -> mutex1");
2223
error_thrown = true;
2324
}
25+
BOOST_CHECK(LockStackEmpty());
2426
#ifdef DEBUG_LOCKORDER
2527
BOOST_CHECK(error_thrown);
2628
#else
@@ -40,9 +42,13 @@ BOOST_AUTO_TEST_CASE(potential_deadlock_detected)
4042

4143
RecursiveMutex rmutex1, rmutex2;
4244
TestPotentialDeadLockDetected(rmutex1, rmutex2);
45+
// The second test ensures that lock tracking data have not been broken by exception.
46+
TestPotentialDeadLockDetected(rmutex1, rmutex2);
4347

4448
Mutex mutex1, mutex2;
4549
TestPotentialDeadLockDetected(mutex1, mutex2);
50+
// The second test ensures that lock tracking data have not been broken by exception.
51+
TestPotentialDeadLockDetected(mutex1, mutex2);
4652

4753
#ifdef DEBUG_LOCKORDER
4854
g_debug_lockorder_abort = prev;

0 commit comments

Comments
 (0)