Make LOCK, LOCK2, TRY_LOCK work with CWaitableCriticalSection#11640
Make LOCK, LOCK2, TRY_LOCK work with CWaitableCriticalSection#11640laanwj merged 4 commits intobitcoin:masterfrom
Conversation
TheBlueMatt
left a comment
There was a problem hiding this comment.
Dont you need a similar DeleteLock() call as CCriticalSection?
| LogPrintf(" %s\n", i.second.ToString()); | ||
| } | ||
| assert(false); | ||
| throw std::logic_error("potential deadlock detected"); |
There was a problem hiding this comment.
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 :/).
There was a problem hiding this comment.
What exactly is the problem? DEBUG_LOCKORDER isn't enabled unless you are doing a special build anyway.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
|
Fails travis due to new lock checking in clang. |
fa4d6b0 to
6e3d96e
Compare
Good catch, added this. |
TheBlueMatt
left a comment
There was a problem hiding this comment.
Awesome, thanks for tackling this.
src/sync.cpp
Outdated
| LogPrintf(" %s\n", i.second.ToString()); | ||
| } | ||
| assert(false); | ||
| if (g_debug_lockorder_abort) abort(); |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
Addressed in 9050eff. (I added fprintf() before the abort following the pattern from AssertLockHeldInternal/AssertLockNotHeldInternal. I can switch to assert though if you prefer that.)
src/sync.h
Outdated
|
|
||
| /** | ||
| * Call abort() if a potential lock order deadlock bug is detected, instead of | ||
| * just logging information and throwing a logic_error. Defaults to true. |
There was a problem hiding this comment.
something something "Only used for unit testing DEBUG_LOCKORDER."
src/sync.h
Outdated
| { | ||
| public: | ||
| ~CCriticalSection() { | ||
| ~LockOrderMixin() { |
There was a problem hiding this comment.
Maybe just move this into AnnotatedMixin instead of making everything two classes on top of each other?
| #define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) | ||
| #define LOCK(cs) DebugLock<decltype(cs)> PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) | ||
| #define LOCK2(cs1, cs2) \ | ||
| DebugLock<decltype(cs1)> criticalblock1(cs1, #cs1, __FILE__, __LINE__); \ |
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Fair enough, I just missed why you changed it, but see it now.
src/sync.cpp
Outdated
| LogPrintf(" %s\n", i.second.ToString()); | ||
| } | ||
| assert(false); | ||
| if (g_debug_lockorder_abort) abort(); |
There was a problem hiding this comment.
Addressed in 9050eff. (I added fprintf() before the abort following the pattern from AssertLockHeldInternal/AssertLockNotHeldInternal. I can switch to assert though if you prefer that.)
src/sync.h
Outdated
|
|
||
| /** | ||
| * Call abort() if a potential lock order deadlock bug is detected, instead of | ||
| * just logging information and throwing a logic_error. Defaults to true. |
src/sync.h
Outdated
| { | ||
| public: | ||
| ~CCriticalSection() { | ||
| ~LockOrderMixin() { |
| #define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) | ||
| #define LOCK(cs) DebugLock<decltype(cs)> PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) | ||
| #define LOCK2(cs1, cs2) \ | ||
| DebugLock<decltype(cs1)> criticalblock1(cs1, #cs1, __FILE__, __LINE__); \ |
There was a problem hiding this comment.
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.
|
utACK 674dcfe |
| /** Interrupt and exit loops */ | ||
| void Interrupt() | ||
| { | ||
| std::unique_lock<std::mutex> lock(cs); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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" ***@***.***> 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:
#11640 (comment)
|
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 ( |
|
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 b6c88a1 -> 41dea6a (pr/dead.9 -> pr/dead.10) due to conflict with #12926 |
| 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. |
src/test/sync_tests.cpp
Outdated
| // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||
|
|
||
| #include "sync.h" | ||
| #include "test/test_bitcoin.h" |
There was a problem hiding this comment.
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 9c4dc59 |
…ection 9c4dc59 Use LOCK macros for non-recursive locks (Russell Yanofsky) 1382913 Make LOCK, LOCK2, TRY_LOCK work with CWaitableCriticalSection (Russell Yanofsky) ba1f095 MOVEONLY Move AnnotatedMixin declaration (Russell Yanofsky) 41b88e9 Add unit test for DEBUG_LOCKORDER code (Russell Yanofsky) Pull request description: Make LOCK macros work with non-recursive mutexes, and use wherever possible for better deadlock detection. Also add unit test for DEBUG_LOCKORDER code. Tree-SHA512: 64ef209307f28ecd0813a283f15c6406138c6ffe7f6cbbd084161044db60e2c099a7d0d2edcd1c5e7770a115e9b931b486e86c9a777bdc96d2e8a9f4dc192942
merge bitcoin#11640, bitcoin#11599, bitcoin#16112, bitcoin#16127, bitcoin#18635, bitcoin#19249: thread safety and locking improvements
Make LOCK macros work with non-recursive mutexes, and use wherever possible for better deadlock detection.
Also add unit test for DEBUG_LOCKORDER code.