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 believe we would sometimes incorrectly catch this - we still have some generic catch blocks lying around in places :(. I wouldn't mind having a policy of only catching in tests, but until then, I think this should, sadly, remain an assert (unless you want to go to the hassle of adding some #define that is only set in test_bitcoin and compiling this unit differently for it :/).
What exactly is the problem? DEBUG_LOCKORDER isn't enabled unless you are doing a special build anyway.
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.
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?
Fails travis due to new lock checking in clang.
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();
I believe abort() doesnt give the same useful stderr print as assert(false) does (specifically giving you a line number so you know at least where the crash was).
Addressed in 9050eff728bd1bbd23aeccfd8cf70b0c999a8e4a. (I added fprintf() before the abort following the pattern from AssertLockHeldInternal/AssertLockNotHeldInternal. I can switch to assert though if you prefer that.)
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.
something something "Only used for unit testing DEBUG_LOCKORDER."
Noted in 9050eff728bd1bbd23aeccfd8cf70b0c999a8e4a
103 | +template <typename Mutex> 104 | +class LockOrderMixin : public Mutex 105 | { 106 | public: 107 | - ~CCriticalSection() { 108 | + ~LockOrderMixin() {
Maybe just move this into AnnotatedMixin instead of making everything two classes on top of each other?
Done in 396911e0e15129c218634c868acb747e423e88d9.
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__); \
Isnt it considered bad practice to turn a #define into two statements (asking, I dont know, I've just always seen people bend over to avoid it).
It's bad practice generally because it can break usages like 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.
Fair enough, I just missed why you changed it, but see it now.
Awesome, thanks for tackling this.
utACK 674dcfea1d0a5314e39fc9731cb863995ff34ea1
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?
We're in a not-great state right now where we have automated lock-checking code, but a bunch of places dont bother to use it. In practice most of those places are really limited use and "obviously-correct" (tm), but that only makes it much easier to break in the future, and making exceptions like that are kinda bad. 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.
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.
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...
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:
DebugLock<CWaitableCriticalSection> lock(cs, "cs", __FILE__, __LINE__);
to replace current master code:
std::unique_lock<std::mutex> lock(cs);
instead of current PR code:
LOCK(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.
Yes, I agree, my comment was somehow out of scope. Let's leave changing the style to something in the future. Or maybe just documenting how the various OS/C++ synchronization primitives map to "bitcoin core style" and vice versa.
Rebased b6c88a148db170367abec036b6c4f2e13104f9aa -> 41dea6a481781e69a1e404a7e3d806f59b47032e (pr/dead.9 -> pr/dead.10) due to conflict with #12926 Rebased 41dea6a481781e69a1e404a7e3d806f59b47032e -> c7c788496ee478064e00b592ac69ac2a23f70e11 (pr/dead.10 -> pr/dead.11) due to conflict with #12743 Rebased c7c788496ee478064e00b592ac69ac2a23f70e11 -> 626de8f3e233ae7c9ffac8b607b21c46b96ccf1e (pr/dead.11 -> pr/dead.12) due to conflict with #13033 Rebased 626de8f3e233ae7c9ffac8b607b21c46b96ccf1e -> 9c4dc597ddc66acfd58a945a5ab11f833731abba (pr/dead.12 -> pr/dead.13) due to conflict with #13423
<!--5d09a71f8925f3f132321140b44b946d-->The last travis run for this pull request was 87 days ago and is thus outdated. To trigger a fresh travis build, this pull request should be closed and re-opened.
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"
Please use #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.
utACK 9c4dc597ddc66acfd58a945a5ab11f833731abba