Make LOCK macros work with non-recursive mutexes, and use wherever possible for better deadlock detection.
Also add unit test for DEBUG_LOCKORDER code.
Make LOCK macros work with non-recursive mutexes, and use wherever possible for better deadlock detection.
Also add unit test for DEBUG_LOCKORDER code.
94@@ -95,7 +95,7 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
95 }
96 LogPrintf(" %s\n", i.second.ToString());
97 }
98- assert(false);
99+ throw std::logic_error("potential deadlock detected");
I presume it wouldn’t actually fail in some cases where a lockorder inversion is hit as the exception here would be caught. Precisely because its (essentially) only enabled in travis we should just keep an assert.
Makes sense, added back an optional abort (on by default), so it will still be guaranteed to fail travis, even in the presence of code that would catch std::logic_error exceptions.
Dont you need a similar DeleteLock() call as CCriticalSection?
Good catch, added this.
94@@ -95,7 +95,8 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
95 }
96 LogPrintf(" %s\n", i.second.ToString());
97 }
98- assert(false);
99+ if (g_debug_lockorder_abort) abort();
77@@ -78,6 +78,13 @@ void LeaveCritical();
78 std::string LocksHeld();
79 void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
80 void DeleteLock(void* cs);
81+
82+/**
83+ * Call abort() if a potential lock order deadlock bug is detected, instead of
84+ * just logging information and throwing a logic_error. Defaults to true.
103+template <typename Mutex>
104+class LockOrderMixin : public Mutex
105 {
106 public:
107- ~CCriticalSection() {
108+ ~LockOrderMixin() {
190-#define LOCK(cs) CCriticalBlock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
191-#define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__)
192-#define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true)
193+#define LOCK(cs) DebugLock<decltype(cs)> PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
194+#define LOCK2(cs1, cs2) \
195+ DebugLock<decltype(cs1)> criticalblock1(cs1, #cs1, __FILE__, __LINE__); \
if (cond) MY_MACRO();
, and the normal workaround is to wrap the statements in a do {...} while(0)
. But the concern doesn’t apply to variable declarations like this.
114@@ -115,7 +115,7 @@ class WorkQueue
115 /** Interrupt and exit loops */
116 void Interrupt()
117 {
118- std::unique_lock<std::mutex> lock(cs);
I don’t like replacing the standard C++11 lock usage here, and in other places, with the macros. The original code seems easier to understand at least.
Edit: so I get that this helps with the lock dependency checking, but I wonder if there isn’t a way that can be done, with keeping the code closer to the original C++11 syntax.
I don’t like replacing the standard C++11 lock usage here, and in other places, with the macros
It doesn’t matter to me which style is used, but the macros provide lock order checking which @TheBlueMatt seemed to think was important. It would be helpful if there could be a guideline for when to use LOCK macros vs standard locks. Is the guideline that you’d recommend just to avoid macros when locks are used with condition variables, or is it broader or narrower than that?
I would strongly prefer we have a consistent syntax across the codebase that uses our lock-checking stuff vs moving back to an ad-hoc world.
This PR switches code to consistently use LOCK syntax, which Wladimir objects to in some cases (not clear which ones).
@laanwj @TheBlueMatt, it’d be good to resolve the apparent disagreement about when to use lock_guard
and when to use LOCK
, if possible. But if there can’t be any agreement, I could revert the changes in the PR to keep preexisting styles.
Yes, I tend to agree with that point. I think just explicitly using our wrapper classes instead of the LOCK macros would suffice there.
On March 2, 2018 6:08:09 PM UTC, “Wladimir J. van der Laan” notifications@github.com wrote:
laanwj commented on this pull request.
@@ -115,7 +115,7 @@ class WorkQueue /** Interrupt and exit loops */ void Interrupt() {
std::unique_lock<std::mutex> lock(cs);
I agree the lock checking is important, but I dislike the non-standard macro syntax as it hides what is happening. So I wondered if there is an official way to ‘decorate’ the C++11 locking primitives, as I doubt we’re the only project to do checking like this. If not, this change is fine with me…
– You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub: https://github.com/bitcoin/bitcoin/pull/11640#discussion_r171920098
Yes, I tend to agree with that point. I think just explicitly using our wrapper classes instead of the LOCK macros would suffice there.
Matt, maybe I’m misunderstanding, but are you suggesting that I write something like:
0DebugLock<CWaitableCriticalSection> lock(cs, "cs", __FILE__, __LINE__);
to replace current master code:
0std::unique_lock<std::mutex> lock(cs);
instead of current PR code:
0LOCK(cs);
If this is the case, I guess I’d prefer to keep the current PR code, since it just uses one style (LOCK
) uniformly and it seems Wladimir no longer objects (https://github.com/bitcoin/bitcoin/pull/11640#discussion_r171920098). In the future, if we want to avoid using LOCK
in some cases without replacing it everywhere, having a documented developer guideline would probably be helpful.
0@@ -0,0 +1,52 @@
1+// Copyright (c) 2012-2017 The Bitcoin Core developers
2+// Distributed under the MIT software license, see the accompanying
3+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+#include "sync.h"
6+#include "test/test_bitcoin.h"
#include <sync.h>
instead.
Move AnnotatedMixin closer to where it's used, and after the DEBUG_LOCKORDER
function declarations so it can call them.
They should also work with any other mutex type which std::unique_lock
supports.
There is no change in behavior for current code that calls these macros with
CCriticalSection mutexes.
Instead of std::unique_lock.