Skip to content

Conversation

@hebasto
Copy link
Member

@hebasto hebasto commented Aug 2, 2020

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::cs an instance of Mutex rather RecursiveMutex.

@hebasto
Copy link
Member Author

hebasto commented Aug 2, 2020

@ajtowns @jnewbery @vasild @JeremyRubin

Mind reviewing?

@JeremyRubin
Copy link
Contributor

this looks correct, concept and like cr ack.

@DrahtBot
Copy link
Contributor

DrahtBot commented Aug 3, 2020

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

No conflicts as of last run.

@jnewbery
Copy link
Contributor

jnewbery commented Aug 3, 2020

Concept ACK. Annotating the existing locking logic is always useful.

TxMempoolInfo CTxMemPool::info(const uint256& txid) const { return info(GenTxid{false, txid}); }
TxMempoolInfo CTxMemPool::info(const uint256& txid) const
{
AssertLockNotHeld(cs);
Copy link
Member

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.

Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Member Author

@hebasto hebasto Aug 3, 2020

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?

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.

Copy link
Contributor

@ajtowns ajtowns left a 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?

TxMempoolInfo CTxMemPool::info(const uint256& txid) const { return info(GenTxid{false, txid}); }
TxMempoolInfo CTxMemPool::info(const uint256& txid) const
{
AssertLockNotHeld(cs);
Copy link
Contributor

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.

@hebasto
Copy link
Member Author

hebasto commented Aug 3, 2020

@ajtowns

Concept ACK. Shouldn't this PR also add GUARDED_BY(cs) to all the other non-static, non-atomic data members?

This PR split out from #19306 to make it small and easy to review.
Making other non-static, non-atomic data members GUARDED_BY(cs) is a design-change decision that I'd leave out of this PR scope.

@practicalswift
Copy link
Contributor

Concept ACK: thanks for adding thread-safety annotations!

@hebasto
Copy link
Member Author

hebasto commented Aug 3, 2020

Updated f084aac -> 343b93b (pr19647.01 -> pr19647.02, diff):

AssertLockHeld is annotated with ASSERT_EXCLUSIVE_LOCK which disables clang's compile time checks that the lock was acquired beforehand. Wouldn't it be better just to rely on the compile time checks?

Copy link
Contributor

@vasild vasild left a 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);
Copy link
Contributor

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.

Copy link
Contributor

@promag promag Aug 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6dbd57f

they get silenced in an unexpected way

@vasild do you have a code sample?

I would say they are an addition

Agree.

Copy link
Contributor

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:

  1. 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);
  1. The presence of ASSERT_EXCLUSIVE_LOCK() alters the behavior of EXCLUSIVE_LOCKS_REQUIRED().

Copy link
Contributor

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.

Copy link
Contributor

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 either LOCK(cs) or AssertLockHeld(cs) to ensure that the mutex is indeed held in all places where ApplyDelta() 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...

Copy link
Contributor

@promag promag left a 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.

AssertLockNotHeld is called immediately in info(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.

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);
Copy link
Contributor

@promag promag Aug 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6dbd57f

they get silenced in an unexpected way

@vasild do you have a code sample?

I would say they are an addition

Agree.

*/
std::map<uint256, uint256> m_unbroadcast_txids GUARDED_BY(cs);

void ClearPrioritisation(const uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3c7d4cb

nit, could refactor to const uint256&?

@hebasto
Copy link
Member Author

hebasto commented Aug 13, 2020

Fellow reviewers! It seems there is a consensus that adding thread safety annotations would be much safer and more convenient after AssertLockHeldInternal() improving. Therefore, begging for #19668 review at first :)

@hebasto
Copy link
Member Author

hebasto commented Sep 1, 2020

Closed in favor of #19854.

@hebasto hebasto closed this Sep 1, 2020
laanwj added a commit that referenced this pull request Sep 4, 2020
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
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Sep 9, 2020
…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
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Feb 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants