What about checking, inside LOCK()
, whether the mutex being locked is instance of Mutex
(and not RecursiveMutex
) and only asserting that we don’t own it in that case:
0diff --git i/src/sync.h w/src/sync.h
1index 60e5a87ae..36c348898 100644
2--- i/src/sync.h
3+++ w/src/sync.h
4@@ -10,12 +10,13 @@
5 #include <util/macros.h>
6
7 #include <condition_variable>
8 #include <mutex>
9 #include <string>
10 #include <thread>
11+#include <type_traits>
12
13 ////////////////////////////////////////////////
14 // //
15 // THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE //
16 // //
17 ////////////////////////////////////////////////
18@@ -145,12 +146,15 @@ private:
19 return Base::owns_lock();
20 }
21
22 public:
23 UniqueLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
24 {
25+ if (std::is_base_of<::Mutex, Mutex>::value) {
26+ AssertLockNotHeldInternal(pszName, pszFile, nLine, &mutexIn);
27+ }
28 if (fTry)
29 TryEnter(pszName, pszFile, nLine);
30 else
31 Enter(pszName, pszFile, nLine);
32 }
33
34diff --git i/src/test/sync_tests.cpp w/src/test/sync_tests.cpp
35index 5c6c2ee38..a66d55519 100644
36--- i/src/test/sync_tests.cpp
37+++ w/src/test/sync_tests.cpp
38@@ -46,7 +46,27 @@ BOOST_AUTO_TEST_CASE(potential_deadlock_detected)
39
40 #ifdef DEBUG_LOCKORDER
41 g_debug_lockorder_abort = prev;
42 #endif
43 }
44
45+template <typename M>
46+void lock(M& m)
47+{
48+ LOCK(m);
49+}
50+
51+BOOST_AUTO_TEST_CASE(double_lock_mutex)
52+{
53+ Mutex m;
54+ LOCK(m);
55+ lock(m);
56+}
57+
58+BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex)
59+{
60+ RecursiveMutex m;
61+ LOCK(m);
62+ lock(m);
63+}
64+
65 BOOST_AUTO_TEST_SUITE_END()
It works - the test double_lock_mutex
fails because of the newly added check while the double_lock_recursive_mutex
succeeds.
The double colon in ::Mutex
is required because the class template type template <typename Mutex, ...> UniqueLock ...
shadows the global typedef ... Mutex;
.