Skip to content

Conversation

ajtowns
Copy link
Contributor

@ajtowns ajtowns commented May 31, 2019

In a few cases we need to use std::mutex rather than the sync.h primitives. But std::lock_guard<std::mutex> doesn't include the clang thread safety annotations unless you also use clang's C library, which means you can't indicate when variables should be guarded by std::mutex mutexes.

This adds an annotated version of std::lock_guard<std::mutex> to threadsafety.h to fix that, and modifies places where std::mutex is used to take advantage of the annotations.

It's based on top of #16112, and turns the thread safety comments included there into annotations.

It also changes the RAII classes in wallet/wallet.h and rpc/blockchain.cpp to just use the atomic flag for synchronisation rather than having a mutex that doesn't actually guard anything as well.

@DrahtBot
Copy link
Contributor

DrahtBot commented May 31, 2019

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

Conflicts

Reviewers, this pull request conflicts with the following ones:

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@practicalswift
Copy link
Contributor

Strong concept ACK: thanks for fixing this!

@promag
Copy link
Contributor

promag commented May 31, 2019

In a few cases we need to use std::mutex rather than the sync.h primitives.

Can you explain why?

@sipa
Copy link
Member

sipa commented May 31, 2019

@promag Our "default" mutexes are recursive mutexes (which you can enter multiple times from the same thread). They're easier to work with as you don't need to worry about whether to call a lock-grabbing function from either inside or outside the lock. However, recursive mutexes can't be used for condition variable waiting, so for those, we use std::mutex (which is not recursive) directly.

Longer term we should try to get rid of recursive mutexes entirely; code is much cleaner without them.

@maflcko
Copy link
Member

maflcko commented May 31, 2019

How is this different from using Mutex and Lock?

E.g.

Mutex m;
LOCK(m);

@promag
Copy link
Contributor

promag commented Jun 2, 2019

@sipa thanks, I was aware of that. I wasn't sure why "In a few cases we need to use std::mutex".

@ajtowns
Copy link
Contributor Author

ajtowns commented Jun 3, 2019

How is this different from using Mutex and Lock?

If you do Mutex cs; instead of std::mutex cs; then you can do LOCK(cs); or WAIT_LOCK(cs, g); instead of MutexGuard g(cs);. In that case the type of g changes from a trivial subclass of std::lock_guard to UniqueLock which is a subclass of std::unique_lock that supports DEBUG_LOCKCONTENTION in which case it uses LogPrintf to report potential deadlocks or lock contention. Even without the extra features we have, std::unique_lock is potentially higher overhead than std::lock_guard, but std::lock_guard isn't unlockable except on destruction, so unique_lock is needed for use with condition variables.

  • We can't use Mutex for locking for the logging API, because that is a circular dependency.
  • We use std::mutex in code that implements our specialised locking behaviour got Mutex etc.
  • We don't use Mutex in support/lockedpool.h, probably because at present it is independent of bitcoin interfaces in general (this PR adds a dependency on threadsafety.h, so maybe just using Mutex would make sense at that point).
  • We use std::mutex instead of Mutex for protecting dir_locks in util/system.h, not sure if there's a circular dependency there to justify it though?
  • We also use std::mutex in some unit tests, which I don't think matters much.

There's some cases where we currently use std::lock_guard<std::mutex> g(cs) where cs is a Mutex, rather than calling LOCK(cs); or WAIT_LOCK(cs, g); That saves a bit of overhead, while losing our contention/deadlock debugging info, which I'm not sure makes much sense. I've updated this PR to change those instances to just use LOCK(cs) instead of lock_guard.

I think that means it makes sense to:

  • Use Mutex and LOCK etc as the "normal" locking mechanism.
  • Use RecursiveMutex (aka CCriticalSection) if recursive locks are needed, or it's old code that we haven't worked through yet.
  • Use std::mutex and { MutexGuard guard(mutex); ... } for code that's used to implement Mutex

I've bumped this PR to replace a couple of the lock_guard instances that referred to our Mutex/CCriticalSection classes with LOCK instead of MutexGuard.

@ryanofsky
Copy link
Contributor

ryanofsky commented Jun 5, 2019

We can't use Mutex for locking for the logging API, because that is a circular dependency.

I don't think this is true (that we can't allow a circular dependency), but maybe you can explain more. I think ideally, we would just use Mutex and RecursiveMutex everywhere, since they fully support LOCK macros, lock assertions, lock annotations, deadlock detection, and so on. What exactly is the need for std::mutex and MutexGuard?

@ajtowns
Copy link
Contributor Author

ajtowns commented Jun 5, 2019

We can't use Mutex for locking for the logging API, because that is a circular dependency.

I don't think this is true (that we can't allow a circular dependency), but maybe you can explain more.

Maybe; LOCK(m); becomes a call to UniqueLock contstructor, which calls Enter() (or TryEnter()), which calls EnterCritical(), which calls push_lock() which may call potential_deadlock_detected() which then calls LogPrintf, which calls LogPrintStr which, when logging to a file, will then lock m_file_mutex. But I don't think that actually causes a problem, because I don't think any lock is ever taken while the m_file_mutex is held, so that shouldn't trigger potential_deadlock_detected.

There's also the case if you compile with -DDEBUG_LOCKCONTENTION. If two processes try to log simultaneously, the first one will lock m_file_mutex successfully, while the second one will fail at the try_lock phase of UniqueLock::Enter, which will then call PrintLockContention and hence LogPrintf, and recursively fail for the same reason until m_file_mutex is released, at which point things should clean up.

I think ideally, we would just use Mutex and RecursiveMutex everywhere, since they fully support LOCK macros, lock assertions, lock annotations, deadlock detection, and so on. What exactly is the need for std::mutex and MutexGuard?

If we can switch all the uses of std::mutex to Mutex, I don't think there's a need for MutexGuard.

@ajtowns
Copy link
Contributor Author

ajtowns commented Feb 11, 2020

Rebased; updated to only use MutexGuard for logging.h.

Remaining uses of std::mutex and lock_guard are sync.h (duh), logging.h (which is used by sync.h), support/lockedpool.h (could be changed to Mutex, but I'm not confident the overhead wouldn't cause problems), tests/checkqueue_tests.cpp (complicated, and just a test) and leveldb.

@ajtowns ajtowns changed the title Add support for thread safety annotations when using std::mutex More thread safety annotation coverage Feb 11, 2020
@ajtowns
Copy link
Contributor Author

ajtowns commented Mar 13, 2020

Rebased to master to avoid #18111 and updated with @sipa's suggestions.

@ajtowns ajtowns marked this pull request as ready for review March 14, 2020 10:56
@ajtowns
Copy link
Contributor Author

ajtowns commented Mar 26, 2020

Rebased for the ToString changes

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.

re-ACK d61883d, the only changes since the previous review are addressed nits:

  • dropped redundant capture in RenameThisThread lambda
  • dropped redundant fScanningWallet = true; assignment

Copy link
Contributor

@ryanofsky ryanofsky 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 d61883d. Would call MutexGuard class LockGuard to make it an obvious replacement for std::lock_guard and use standard names for things where Mutex is the thing being locked, and Lock is the object locking and unlocking it

ajtowns added 2 commits May 26, 2020 23:23
Adds LockGuard helper in threadsafety.h to replace lock_guard<mutex>
when LOCK(Mutex) isn't available for use.
@ajtowns
Copy link
Contributor Author

ajtowns commented May 26, 2020

Renamed MutexGuard to LockGuard, and dropped util_threadnames_tests changes.

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.

re-ACK 5478d6c, only renamed s/MutexGuard/LockGuard/, and dropped the commit "test/util_threadnames_tests: add thread safety annotations" since the previous review.

Copy link
Contributor

@ryanofsky ryanofsky 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 5478d6c. Thanks for taking suggestions! Only changes since last review are dropping thread rename test commit d53072e and renaming mutex guard to lock guard

@maflcko
Copy link
Member

maflcko commented May 27, 2020

ACK 5478d6c 🗾

Show signature and timestamp

Signature:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

ACK 5478d6c099e76fe070703cc5383cba7b91468b0f 🗾
-----BEGIN PGP SIGNATURE-----

iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
pUiM+Qv/bctxRqxjmwEq2xf4FSS7ej1ao6ni8G8tnk9+sB1QniYItq56EOxEe99u
lEB6lt7QFL2z9fD/mH6gEp5mdM0yqt16z35+MGxxVYNrBX0Lm/8lquFqHlPA5DNF
mfUp7Ro82ixyc65ZlMakjkgY5lr1rrNDPB9XWpw5jKFTvc5b2Dv+n7FxshM77BXd
1U8tgAOviw8etuhIWDSoTSVj85ubx9ld0cualiKg55prHi1gyH7zEQrOTJ4TYiS7
R8vPpnvK/nKpnbUq/jbXXT9DgV+3O0nKfSA+uipzsG0417ON83ugewko8oOQaP19
NtJWThsr1Eijk/YiPpdSpdNX2s0xqcvRTGaxEFU3Vwh80x4p3If2pPI96izs5Ye5
HyEGjZDtZRfco27eUp+YHPM8pk8Fg5pAJqdTQLfkGR3FTgR+r6PuXJ2GkSkXqHKv
Plx/qOYuSNlDJMd1dyCCeW6GiEpNh2XuszgldLIB8pjSCaq8qjSJtep+wmEIdsh/
ueOKDaHz
=Ww4X
-----END PGP SIGNATURE-----

Timestamp of file with hash 0b593eb159203e57d289eb41d38e7938cdd641ec1ddca7c9967f9f58ff7986de -

@maflcko maflcko merged commit 55b4c65 into bitcoin:master May 27, 2020
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request May 28, 2020
5478d6c logging: thread safety annotations (Anthony Towns)
e685ca1 util/system.cpp: add thread safety annotations for dir_locks (Anthony Towns)
a788789 test/checkqueue_tests: thread safety annotations (Anthony Towns)
479c584 rpc/blockchain.cpp: thread safety annotations for latestblock (Anthony Towns)
8b5af3d net: fMsgProcWake use LOCK instead of lock_guard (Anthony Towns)
de7c5f4 wallet/wallet.h: Remove mutexScanning which was only protecting a single atomic bool (Anthony Towns)
c3cf2f5 rpc/blockchain.cpp: Remove g_utxosetscan mutex that is only protecting a single atomic variable (Anthony Towns)

Pull request description:

  In a few cases we need to use `std::mutex` rather than the sync.h primitives. But `std::lock_guard<std::mutex>` doesn't include the clang thread safety annotations unless you also use clang's C library, which means you can't indicate when variables should be guarded by `std::mutex` mutexes.

  This adds an annotated version of `std::lock_guard<std::mutex>` to threadsafety.h to fix that, and modifies places where `std::mutex` is used to take advantage of the annotations.

  It's based on top of bitcoin#16112, and turns the thread safety comments included there into annotations.

  It also changes the RAII classes in wallet/wallet.h and rpc/blockchain.cpp to just use the atomic<bool> flag for synchronisation rather than having a mutex that doesn't actually guard anything as well.

ACKs for top commit:
  MarcoFalke:
    ACK 5478d6c 🗾
  hebasto:
    re-ACK 5478d6c, only renamed s/`MutexGuard`/`LockGuard`/, and dropped the commit "test/util_threadnames_tests: add thread safety annotations" since the [previous](bitcoin#16127 (review)) review.
  ryanofsky:
    Code review ACK 5478d6c. Thanks for taking suggestions! Only changes since last review are dropping thread rename test commit d53072e and renaming mutex guard to lock guard

Tree-SHA512: 7b00d31f6f2b5a222ec69431eb810a74abf0542db3a65d1bbad54e354c40df2857ec89c00b4a5e466c81ba223267ca95f3f98d5fbc1a1d052a2c3a7d2209790a
deadalnix pushed a commit to Bitcoin-ABC/bitcoin-abc that referenced this pull request Sep 23, 2020
Summary:
 * rpc/blockchain.cpp: Remove g_utxosetscan mutex that is only protecting a single atomic variable

 * wallet/wallet.h: Remove mutexScanning which was only protecting a single atomic bool

 * net: fMsgProcWake use LOCK instead of lock_guard

 * rpc/blockchain.cpp: thread safety annotations for latestblock

 * test/checkqueue_tests: thread safety annotations

 * util/system.cpp: add thread safety annotations for dir_locks

 * logging: thread safety annotations

Adds LockGuard helper in threadsafety.h to replace lock_guard<mutex>
when LOCK(Mutex) isn't available for use.

This is a backport of Core [[bitcoin/bitcoin#16127 | PR16127]]

Test Plan:
  ninja all check

Using clang

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Differential Revision: https://reviews.bitcoinabc.org/D7523
kwvg added a commit to kwvg/dash that referenced this pull request Jun 5, 2021
kwvg added a commit to kwvg/dash that referenced this pull request Jun 6, 2021
kwvg added a commit to kwvg/dash that referenced this pull request Jun 6, 2021
kwvg added a commit to kwvg/dash that referenced this pull request Jun 6, 2021
UdjinM6 pushed a commit to UdjinM6/dash that referenced this pull request Jun 6, 2021
UdjinM6 pushed a commit to UdjinM6/dash that referenced this pull request Jun 6, 2021
kwvg added a commit to kwvg/dash that referenced this pull request Jun 7, 2021
kwvg added a commit to kwvg/dash that referenced this pull request Jun 8, 2021
kwvg added a commit to kwvg/dash that referenced this pull request Jun 9, 2021
UdjinM6 added a commit to dashpay/dash that referenced this pull request Jun 11, 2021
@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.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants