validation: use stronger EXCLUSIVE_LOCKS_REQUIRED()#24235
validation: use stronger EXCLUSIVE_LOCKS_REQUIRED()#24235maflcko merged 1 commit intobitcoin:masterfrom
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
There was a problem hiding this comment.
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.
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.
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 (5730f7327f) as it provided stronger guarantees, but there is some frowning against using |
There was a problem hiding this comment.
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.
hebasto
left a comment
There was a problem hiding this comment.
ACK 99de806.
Some note for the future:
clang-format-diff.pyfails 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.
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_mutexatthe 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.