Skip to content
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

Replace lock with thread safety annotation in CBlockTreeDB::LoadBlockIndexGuts() #24197

Conversation

jonatack
Copy link
Member

Following up on #22932 (comment) by Marco Falke (good observation, thank you), we can replace a cs_main lock in CBlockTreeDB::LoadBlockIndexGuts() with a Clang thread safety annotation/assertion instead. The unlocked code is reverted to its original state before #22932.

Copy link
Member

@hebasto hebasto left a comment

Choose a reason for hiding this comment

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

ACK 20276ca, I have reviewed the code and it looks OK, I agree it can be merged.

Copy link
Contributor

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

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

utACK, I reviewed the code; I agree this is safe, and can be merged.

From a practical perspective, I wonder if this change (or changes like it) would see a performance gain, or if the goal is more about clean code. I also wonder if it is will result in future changes that are sub-optimal. What are the odds that something will want to call this function in the future that is not already locking cs_main?

@jonatack
Copy link
Member Author

jonatack commented Jan 30, 2022

Thanks for reviewing!

From a practical perspective, I wonder if this change (or changes like it) would see a performance gain, or if the goal is more about clean code.

I think the goal is to remove any unneeded locks for performance, while using locks where needed for thread safety. If you launch bitcoind with debug=lock (or run bitcoin-cli logging [] '["lock"]' on a bitcoind instance) you'll see how much time is spent in lock contention, and in some cases it is significant. Even in faster or non-hotspot cases, taking a lock, if contended, isn't particularly cheap so it seems best to not do so needlessly.

I also wonder if it is will result in future changes that are sub-optimal. What are the odds that something will want to call this function in the future that is not already locking cs_main?

In this case, thanks to the thread safety annotation the compiler would give a warning, and the run-time assertion may be hit, both of which would serve to warn that a missing lock is needed. At this time, however, LoadBlockIndexGuts() only has one caller in the codebase.

More information about Clang thread safety analysis, if helpful:
- https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
- https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#lockingmutex-usage-notes
- https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#threads-and-synchronization

@PastaPastaPasta
Copy link
Contributor

Thanks for the response @jonatack completely agree with minimizing the time spent in critical sections (and doing so saftely)

I have a few questions if you'd be so kind :)

How expensive is locking a RecursiveMutex, if the same thread already has acquired it? Is it the same cost as acquiring an unlocked recursive mutex, or basically a no-op?

My concern is not a future caller would trigger a data-race / thread insafety, the annotations / assertions prevent that. My question is more along the line of, if we introduce a call in the future in some function WITH_LOCK(::cs_main, LoadBlockIndexGuts...); then the scope of the cs_main lock will be broader than needed correct? IE, inside of LoadBlockIndexGuts, only four lines required the cs_main lock, but we were forced to lock cs_main for the entire function call. Does that make sense?

Thanks

@maflcko
Copy link
Member

maflcko commented Jan 31, 2022

This is only called once in init, which already holds cs_main. It doesn't really make sense to call this anywhere in a hot loop

@maflcko maflcko merged commit 4efdbab into bitcoin:master Jan 31, 2022
@jonatack jonatack deleted the replace-lock-with-annotation-in-LoadBlockIndexGuts branch January 31, 2022 08:08
@jonatack
Copy link
Member Author

jonatack commented Jan 31, 2022

@PastaPastaPasta if you'd like to dive deeper, have a look at src/sync.{h,cpp}, https://preshing.com/20111118/locks-arent-slow-lock-contention-is/, and maybe the other links in #22736 (comment). I updated my reply above to mention contention. There are also system calls, memory/cache coherency and fences, and wait queues, for example, in addition to lock contentions.

sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Feb 1, 2022
…lockTreeDB::LoadBlockIndexGuts()

20276ca Replace lock with thread safety annotation in CBlockTreeDB::LoadBlockIndexGuts() (Jon Atack)

Pull request description:

  Following up on bitcoin#22932 (comment) by Marco Falke (good observation, thank you), we can replace a cs_main lock in `CBlockTreeDB::LoadBlockIndexGuts()` with a Clang thread safety annotation/assertion instead. The unlocked code is reverted to its original state before bitcoin#22932.

ACKs for top commit:
  hebasto:
    ACK 20276ca, I have reviewed the code and it looks OK, I agree it can be merged.

Tree-SHA512: 2d91d1c962af0286d835d92a56396a27ea00e9d061b869eaff9421b448a083b0513828e1d4df7504396896057bf1e344e180a50271a5cfd1e1c7b6527155b2bb
Fabcien pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Jan 24, 2023
Summary:
> CBlockIndex::nStatus may be racy, i.e. potentially accessed by multiple threads, see [[bitcoin/bitcoin#17161 | core#17161]]. A solution is to guard it by cs_main, along with fellow data members nFile, nDataPos and nUndoPos.

Co-authored-by: Vasil Dimov <[email protected]>

This concludes backport of [[bitcoin/bitcoin#22932 | core#22932]] and  [[bitcoin/bitcoin#24197 | core#24197]]
bitcoin/bitcoin@6ea5682
bitcoin/bitcoin@20276ca

Depends on D13037

Test Plan:
With clang and DEBUG:

`ninja all check-all`

Reviewers: #bitcoin_abc, Fabien

Reviewed By: #bitcoin_abc, Fabien

Subscribers: Fabien

Differential Revision: https://reviews.bitcoinabc.org/D13038
@bitcoin bitcoin locked and limited conversation to collaborators Jan 31, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants