-
Notifications
You must be signed in to change notification settings - Fork 38.7k
Add thread safety annotations to CTxMemPool methods #19647
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
|
@ajtowns @jnewbery @vasild @JeremyRubin Mind reviewing? |
|
this looks correct, concept and like cr ack. |
|
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsNo conflicts as of last run. |
|
Concept ACK. Annotating the existing locking logic is always useful. |
src/txmempool.cpp
Outdated
| TxMempoolInfo CTxMemPool::info(const uint256& txid) const { return info(GenTxid{false, txid}); } | ||
| TxMempoolInfo CTxMemPool::info(const uint256& txid) const | ||
| { | ||
| AssertLockNotHeld(cs); |
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.
Excluding locks in getters makes it impossible to get more than one consistent mempool entry, as the state of the second might change after the first one has been returned.
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.
AssertLockNotHeld is called immediately in info(gtxid) seems unnecessary to repeat it?
We've got infoAll for a consistent look at all mempool entries (used by DumpMempool), and apparently don't use info for consistent views otherwise, so excluding the lock seems okay to me.
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.
To get consistent values the external locking of CTxMemPool::cs should be used.
Anyway, this PR does not change locking behavior.
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.
AssertLockNotHeldis called immediately ininfo(gtxid)seems unnecessary to repeat it?
When a thread safety annotation is a part of function member declaration, I found it very convenient having a corresponding AssertLock{Not}Held() statement in its definition.
Also this assertion would be really helpful when CTxMemPool::cs will actually transit from RecursiveMutex to Mutex.
ajtowns
left a comment
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.
Concept ACK. Shouldn't this PR also add GUARDED_BY(cs) to all the other non-static, non-atomic data members?
src/txmempool.cpp
Outdated
| TxMempoolInfo CTxMemPool::info(const uint256& txid) const { return info(GenTxid{false, txid}); } | ||
| TxMempoolInfo CTxMemPool::info(const uint256& txid) const | ||
| { | ||
| AssertLockNotHeld(cs); |
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.
AssertLockNotHeld is called immediately in info(gtxid) seems unnecessary to repeat it?
We've got infoAll for a consistent look at all mempool entries (used by DumpMempool), and apparently don't use info for consistent views otherwise, so excluding the lock seems okay to me.
This PR split out from #19306 to make it small and easy to review. |
|
Concept ACK: thanks for adding thread-safety annotations! |
|
Updated f084aac -> 343b93b (pr19647.01 -> pr19647.02, diff):
|
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.
Looks good except the suggestion below.
It was tedious to check the callers of e.g. ApplyDelta() and their callers and their callers etc until finding either LOCK(cs) or AssertLockHeld(cs) to ensure that the mutex is indeed held in all places where ApplyDelta() is called.
|
|
||
| void CTxMemPool::ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const | ||
| { | ||
| LOCK(cs); |
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.
Here and in CTxMemPool::ClearPrioritisation(), CTxMemPool::GetTotalTxSize() and CTxMemPool::IsUnbroadcastTx() I would strongly advise to add AssertLockHeld(cs) where LOCK(cs) is removed. And in general in every function that is annotated EXCLUSIVE_LOCKS_REQUIRED(). It would serve as documentation to the reader and also ensure that the code does not get executed without holding the mutex.
The clang compile time checks are good, but I would say not to be the sole protection - they get silenced in an unexpected way if EXCLUSIVE_LOCKS_REQUIRED() and ASSERT_EXCLUSIVE_LOCK() are both used, do not work with gcc and produce only compile warnings with clang (could be missed if -Werror is not used). I would say they are an addition, but not replacement of run-time asserts.
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.
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.
they get silenced in an unexpected way
@vasild do you have a code sample?
Yes, see #19668 (comment). There are two unexpected things:
- The warnings differ depending on the order of the attributes (if more than one is specified):
void f() EXCLUSIVE_LOCKS_REQUIRED(cs) ASSERT_EXCLUSIVE_LOCK(cs);
// vs
void f() ASSERT_EXCLUSIVE_LOCK(cs) EXCLUSIVE_LOCKS_REQUIRED(cs);- The presence of
ASSERT_EXCLUSIVE_LOCK()alters the behavior ofEXCLUSIVE_LOCKS_REQUIRED().
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.
Annotating both ASSERT_EXCLUSIVE_LOCK and EXCLUSIVE_LOCKS_REQUIRED on the same function makes no sense. The only reason to do the ASSERT_EXCLUSIVE_LOCK is if you're relying on the test having succeeded for execution to continue as far as later compile-time checks are concerned, but if you've satisfied EXCLUSIVE_LOCKS_REQUIRED already, then there's no need to hint the later compile-time checks, they're already satisfied.
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.
It was tedious to check the callers of e.g.
ApplyDelta()and their callers and their callers etc until finding eitherLOCK(cs)orAssertLockHeld(cs)to ensure that the mutex is indeed held in all places whereApplyDelta()is called.
The whole point of the compile time annotations is to have the compiler do the work of checking that the callers have a LOCK(cs). Note that an AssertLockHeld(cs) isn't sufficient -- any time that's called without having the lock already held would also be a bug; and until the ASSERT_EXCLUSIVE_LOCK annotation is removed from AssertLockHeld, AssertLockHeld may in fact prevent the compiler from doing adequate checking...
promag
left a comment
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.
Code review ACK 343b93b.
AssertLockNotHeldis called immediately ininfo(gtxid)seems unnecessary to repeat it?When a thread safety annotation is a part of function member declaration, I found it very convenient having a corresponding
AssertLock{Not}Held()statement in its definition.Also this assertion would be really helpful when
CTxMemPool::cswill actually transit fromRecursiveMutextoMutex.
Let's not make a wave of pulls adding negative thread safety annotations everywhere.
|
|
||
| void CTxMemPool::ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const | ||
| { | ||
| LOCK(cs); |
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.
| */ | ||
| std::map<uint256, uint256> m_unbroadcast_txids GUARDED_BY(cs); | ||
|
|
||
| void ClearPrioritisation(const uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs); |
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.
nit, could refactor to const uint256&?
|
Fellow reviewers! It seems there is a consensus that adding thread safety annotations would be much safer and more convenient after |
|
Closed in favor of #19854. |
020f051 refactor: CTxMemPool::IsUnbroadcastTx() requires CTxMemPool::cs lock (Hennadii Stepanov) 7c4bd03 refactor: CTxMemPool::GetTotalTxSize() requires CTxMemPool::cs lock (Hennadii Stepanov) fa5fcb0 refactor: CTxMemPool::ClearPrioritisation() requires CTxMemPool::cs lock (Hennadii Stepanov) 7140b31 refactor: CTxMemPool::ApplyDelta() requires CTxMemPool::cs lock (Hennadii Stepanov) 66e47e5 refactor: CTxMemPool::UpdateChild() requires CTxMemPool::cs lock (Hennadii Stepanov) 9398077 refactor: CTxMemPool::UpdateParent() requires CTxMemPool::cs lock (Hennadii Stepanov) Pull request description: This is another step to transit `CTxMemPool::cs` from `RecursiveMutex` to `Mutex`. Split out from #19306. Only trivial thread safety annotations and lock assertions added. No new locks. No behavior change. Refactoring `const uint256` to `const uint256&` was [requested](#19647 (comment)) by **promag**. Please note that now, since #19668 has been merged, it is safe to apply `AssertLockHeld()` macros as they do not swallow compile time Thread Safety Analysis warnings. ACKs for top commit: promag: Core review ACK 020f051. jnewbery: Code review ACK 020f051 vasild: ACK 020f051 Tree-SHA512: a31e389142d5a19b25fef0aaf1072a337278564528b5cc9209df88ae548a31440e1b8dd9bae0169fd7aa59ea06e22fe5e0413955386512b83ef1f3e7d941e890
…le cases 020f051 refactor: CTxMemPool::IsUnbroadcastTx() requires CTxMemPool::cs lock (Hennadii Stepanov) 7c4bd03 refactor: CTxMemPool::GetTotalTxSize() requires CTxMemPool::cs lock (Hennadii Stepanov) fa5fcb0 refactor: CTxMemPool::ClearPrioritisation() requires CTxMemPool::cs lock (Hennadii Stepanov) 7140b31 refactor: CTxMemPool::ApplyDelta() requires CTxMemPool::cs lock (Hennadii Stepanov) 66e47e5 refactor: CTxMemPool::UpdateChild() requires CTxMemPool::cs lock (Hennadii Stepanov) 9398077 refactor: CTxMemPool::UpdateParent() requires CTxMemPool::cs lock (Hennadii Stepanov) Pull request description: This is another step to transit `CTxMemPool::cs` from `RecursiveMutex` to `Mutex`. Split out from bitcoin#19306. Only trivial thread safety annotations and lock assertions added. No new locks. No behavior change. Refactoring `const uint256` to `const uint256&` was [requested](bitcoin#19647 (comment)) by **promag**. Please note that now, since bitcoin#19668 has been merged, it is safe to apply `AssertLockHeld()` macros as they do not swallow compile time Thread Safety Analysis warnings. ACKs for top commit: promag: Core review ACK 020f051. jnewbery: Code review ACK 020f051 vasild: ACK 020f051 Tree-SHA512: a31e389142d5a19b25fef0aaf1072a337278564528b5cc9209df88ae548a31440e1b8dd9bae0169fd7aa59ea06e22fe5e0413955386512b83ef1f3e7d941e890
Split out from #19306.
Only trivial thread safety annotations and lock assertions added. No new locks. No behavior change.
This is a step to make
CTxMemPool::csan instance ofMutexratherRecursiveMutex.