-
Notifications
You must be signed in to change notification settings - Fork 37.8k
validation: use stronger EXCLUSIVE_LOCKS_REQUIRED() #24235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
I'm a bit vague about using negated annotation for global mutexes, in particular Btw, which clang versions were tested? |
I tested with clang 14. |
src/validation.h
Outdated
@@ -639,7 +640,8 @@ class CChainState | |||
*/ | |||
bool ActivateBestChain( | |||
BlockValidationState& state, | |||
std::shared_ptr<const CBlock> pblock = nullptr) LOCKS_EXCLUDED(m_chainstate_mutex, cs_main); | |||
std::shared_ptr<const CBlock> pblock = nullptr) | |||
EXCLUSIVE_LOCKS_REQUIRED(!::cs_main, !m_chainstate_mutex); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tend toward NACK using negative annotations with global mutexes, especially cs_main. I presume LOCKS_EXCLUDED(::cs_main) EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex)
doesn't work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's wrong with using negative annotations with global mutexes? I don't see how the scope of the mutex is relevant, but maybe I miss something since @hebasto also wasn't excited about it. Can you elaborate?
I presume
LOCKS_EXCLUDED(::cs_main) EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex)
doesn't work?
It works. Updated the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate?
See the commit that you ACKed: 9ac8f6d
- It is wasted effort, since cs_main is recursive
- It is impossible to properly implement: Add missing thread safety annotations #20272 (comment)
- It is too verbose for globals, as they propagate outside the scope of "their"
class
Shouldn't |
No. It shouldn't. It gets too noisy with warnings about Sorry. |
Restarted some CI jobs that failed due to connectivity issues. |
bitcoin#24103 added annotations to denote that the callers of `CChainState::ActivateBestChain()` and `CChainState::InvalidateBlock()` must not own `m_chainstate_mutex` at the time of the call. Replace the added `LOCKS_EXCLUDED()` with a stronger `EXCLUSIVE_LOCKS_REQUIRED()`, see https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#negative for the difference between both.
5730f73
to
99de806
Compare
Concept ACK. |
I think the previous version was better (5730f73) as it provided stronger guarantees, but there is some frowning against using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tested 99de806 with Ubuntu clang version 12.0.0-3ubuntu1~20.04.4
.
- The following testing patch:
diff --git a/src/validation.cpp b/src/validation.cpp
index c12dc9e8b..2f8e8c0c9 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -2845,6 +2845,12 @@ static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
}
}
+void CChainState::foo()
+{
+ BlockValidationState state;
+ InvalidateBlock(state, nullptr);
+}
+
bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr<const CBlock> pblock)
{
AssertLockNotHeld(m_chainstate_mutex);
diff --git a/src/validation.h b/src/validation.h
index fdfd29d1f..7bdc8aae3 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -755,6 +755,8 @@ private:
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
friend ChainstateManager;
+
+ void foo();
};
/**
results in:
$ make
...
validation.cpp:2851:5: warning: calling function 'InvalidateBlock' requires negative capability '!m_chainstate_mutex' [-Wthread-safety-analysis]
InvalidateBlock(state, nullptr);
^
1 warning generated
...
- And the following testing patch:
diff --git a/src/validation.cpp b/src/validation.cpp
index c12dc9e8b..84f99312b 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -2845,6 +2845,12 @@ static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
}
}
+void CChainState::foo()
+{
+ BlockValidationState state;
+ InvalidateBlock(state, nullptr);
+}
+
bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr<const CBlock> pblock)
{
AssertLockNotHeld(m_chainstate_mutex);
@@ -2861,6 +2867,8 @@ bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr
// we use m_chainstate_mutex to enforce mutual exclusion so that only one caller may execute this function at a time
LOCK(m_chainstate_mutex);
+ foo();
+
CBlockIndex *pindexMostWork = nullptr;
CBlockIndex *pindexNewTip = nullptr;
int nStopAtHeight = gArgs.GetIntArg("-stopatheight", DEFAULT_STOPATHEIGHT);
diff --git a/src/validation.h b/src/validation.h
index fdfd29d1f..39102ba43 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -755,6 +755,8 @@ private:
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
friend ChainstateManager;
+
+ void foo() EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex);
};
/**
causes the following warning:
$ make
...
validation.cpp:2870:5: warning: cannot call function 'foo' while mutex 'm_chainstate_mutex' is held [-Wthread-safety-analysis]
foo();
^
1 warning generated.
...
The similar patch being applied to the master branch does not fire any thread-safety warnings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 99de806.
Some note for the future:
clang-format-diff.py
fails to handle multiple multi-line TS annotations- Should we recommend to use negative capability forms of TS annotations for member mutexes in new code?
If a method does |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 99de806. Tested with Debian clang version 13.0.1. Reproduced hebasto's results. Verified that LoadExternalBlockFile()
needs the annotation added here.
Should the following run-time thread safety assertions be added?
suggested run-time assertions
diff --git a/src/validation.cpp b/src/validation.cpp
index c12dc9e8b6..109b1f95ce 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -2848,6 +2848,7 @@ static void LimitValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main) {
bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr<const CBlock> pblock)
{
AssertLockNotHeld(m_chainstate_mutex);
+ AssertLockNotHeld(::cs_main);
@@ -2949,6 +2950,8 @@ bool CChainState::ActivateBestChain(BlockValidationState& state, std::shared_ptr
bool CChainState::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex)
{
+ AssertLockNotHeld(m_chainstate_mutex);
+ AssertLockNotHeld(::cs_main);
{
LOCK(cs_main);
@@ -2979,6 +2982,7 @@ bool CChainState::PreciousBlock(BlockValidationState& state, CBlockIndex* pindex
bool CChainState::InvalidateBlock(BlockValidationState& state, CBlockIndex* pindex)
{
AssertLockNotHeld(m_chainstate_mutex);
+ AssertLockNotHeld(::cs_main);
@@ -4089,6 +4093,8 @@ bool CChainState::LoadGenesisBlock()
void CChainState::LoadExternalBlockFile(FILE* fileIn, FlatFilePos* dbp)
{
+ AssertLockNotHeld(m_chainstate_mutex);
(edited OP and review comments) |
Thanks @MarcoFalke, left an @-reference in my ACK. Added the thread safety assertions in my comment in #24235 (review) to #24177. |
Summary: ``` So apparently there is no recursion involved, so the m_cs_chainstate can be a non-recursive mutex. ``` Backport of [[bitcoin/bitcoin#24103 | core#24103]]. Backport of [[bitcoin/bitcoin#24235 | core#24235]]. Test Plan: With Debug and Clang: ninja all check Run the TSAN build. Reviewers: #bitcoin_abc, PiRK Reviewed By: #bitcoin_abc, PiRK Differential Revision: https://reviews.bitcoinabc.org/D12227
#24103 added annotations to
denote that the callers of
CChainState::ActivateBestChain()
andCChainState::InvalidateBlock()
must not ownm_chainstate_mutex
atthe time of the call.
Replace the added
LOCKS_EXCLUDED()
with a strongerEXCLUSIVE_LOCKS_REQUIRED()
, seehttps://clang.llvm.org/docs/ThreadSafetyAnalysis.html#negative for the
difference between both.