2012-05-11 17:00:03 +02:00
|
|
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
2023-04-25 13:51:26 +02:00
|
|
|
// Copyright (c) 2009-2020 The Bitcoin Core developers
|
2014-12-13 05:09:33 +01:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
2012-05-18 16:02:28 +02:00
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2013-04-13 07:13:08 +02:00
|
|
|
|
2012-05-11 17:00:03 +02:00
|
|
|
#ifndef BITCOIN_SYNC_H
|
|
|
|
#define BITCOIN_SYNC_H
|
|
|
|
|
2021-09-09 15:55:03 +02:00
|
|
|
#include <logging.h>
|
|
|
|
#include <logging/timer.h>
|
2020-03-19 23:46:56 +01:00
|
|
|
#include <threadsafety.h>
|
2019-09-25 16:55:52 +02:00
|
|
|
#include <util/macros.h>
|
2013-04-13 07:13:08 +02:00
|
|
|
|
2017-11-07 19:29:21 +01:00
|
|
|
#include <condition_variable>
|
|
|
|
#include <mutex>
|
2024-03-20 20:38:58 +01:00
|
|
|
#include <shared_mutex>
|
2020-03-16 12:45:38 +01:00
|
|
|
#include <string>
|
|
|
|
#include <thread>
|
2013-06-24 18:10:15 +02:00
|
|
|
|
2016-04-14 23:28:33 +02:00
|
|
|
/////////////////////////////////////////////////
|
|
|
|
// //
|
2015-08-09 01:17:27 +02:00
|
|
|
// THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE //
|
2016-04-14 23:28:33 +02:00
|
|
|
// //
|
|
|
|
/////////////////////////////////////////////////
|
2013-06-24 18:10:15 +02:00
|
|
|
|
|
|
|
/*
|
2019-01-15 19:41:59 +01:00
|
|
|
RecursiveMutex mutex;
|
2017-11-07 19:29:21 +01:00
|
|
|
std::recursive_mutex mutex;
|
2013-06-24 18:10:15 +02:00
|
|
|
|
|
|
|
LOCK(mutex);
|
2017-11-07 19:29:21 +01:00
|
|
|
std::unique_lock<std::recursive_mutex> criticalblock(mutex);
|
2013-06-24 18:10:15 +02:00
|
|
|
|
|
|
|
LOCK2(mutex1, mutex2);
|
2017-11-07 19:29:21 +01:00
|
|
|
std::unique_lock<std::recursive_mutex> criticalblock1(mutex1);
|
|
|
|
std::unique_lock<std::recursive_mutex> criticalblock2(mutex2);
|
2013-06-24 18:10:15 +02:00
|
|
|
|
|
|
|
TRY_LOCK(mutex, name);
|
2017-11-07 19:29:21 +01:00
|
|
|
std::unique_lock<std::recursive_mutex> name(mutex, std::try_to_lock_t);
|
2013-06-24 18:10:15 +02:00
|
|
|
|
|
|
|
ENTER_CRITICAL_SECTION(mutex); // no RAII
|
|
|
|
mutex.lock();
|
|
|
|
|
|
|
|
LEAVE_CRITICAL_SECTION(mutex); // no RAII
|
|
|
|
mutex.unlock();
|
|
|
|
*/
|
|
|
|
|
|
|
|
///////////////////////////////
|
|
|
|
// //
|
|
|
|
// THE ACTUAL IMPLEMENTATION //
|
|
|
|
// //
|
|
|
|
///////////////////////////////
|
|
|
|
|
2017-11-08 21:28:35 +01:00
|
|
|
#ifdef DEBUG_LOCKORDER
|
2020-11-25 17:01:57 +01:00
|
|
|
template <typename MutexType>
|
|
|
|
void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false);
|
2017-11-08 21:28:35 +01:00
|
|
|
void LeaveCritical();
|
2020-03-06 20:47:49 +01:00
|
|
|
void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
|
2017-11-08 21:28:35 +01:00
|
|
|
std::string LocksHeld();
|
2021-06-09 14:02:42 +02:00
|
|
|
template <typename MutexType>
|
Merge #19668: Do not hide compile-time thread safety warnings
ea74e10acf17903e44c85e3678853414653dd4e1 doc: Add best practice for annotating/asserting locks (Hennadii Stepanov)
2ee7743fe723227f2ea1b031eddb14fc6863f4c8 sync.h: Make runtime lock checks require compile-time lock checks (Anthony Towns)
23d71d171e6e22ba5e4a909d597a54595b2a2c1f Do not hide compile-time thread safety warnings (Hennadii Stepanov)
3ddc150857178bfb1c854c05bf9b526777876f56 Add missed thread safety annotations (Hennadii Stepanov)
af9ea55a72c94678b343f5dd98dc78f3a3ac58cb Use LockAssertion utility class instead of AssertLockHeld() (Hennadii Stepanov)
Pull request description:
On the way of transit from `RecursiveMutex` to `Mutex` (see #19303) it is crucial to have run-time `AssertLockHeld()` assertion that does _not_ hide compile-time Clang Thread Safety Analysis warnings.
On master (65e4ecabd5b4252154640c7bac38c92a3f3a7018) using `AssertLockHeld()` could hide Clang Thread Safety Analysis warnings, e.g., with the following patch applied:
```diff
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -607,7 +607,7 @@ public:
void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
- void removeForReorg(const CCoinsViewCache* pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
+ void removeForReorg(const CCoinsViewCache* pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight) EXCLUSIVE_LOCKS_REQUIRED(cs);
```
Clang compiles the code without any thread safety warnings.
See "Add missed thread safety annotations" commit for the actual thread safety warnings that are fixed in this PR.
ACKs for top commit:
MarcoFalke:
ACK ea74e10acf 🎙
jnewbery:
ACK ea74e10acf17903e44c85e3678853414653dd4e1
ajtowns:
ACK ea74e10acf17903e44c85e3678853414653dd4e1
Tree-SHA512: 8cba996e526751a1cb0e613c0cc1b10f027a3e9945fbfb4bd30f6355fd36b9f9c2e1e95ed3183fc254b42df7c30223278e18e5bdb5e1ef85db7fef067595d447
2020-09-01 08:18:20 +02:00
|
|
|
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs);
|
|
|
|
template <typename MutexType>
|
2021-04-06 07:54:07 +02:00
|
|
|
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs);
|
2017-11-08 21:28:35 +01:00
|
|
|
void DeleteLock(void* cs);
|
2020-06-22 20:12:14 +02:00
|
|
|
bool LockStackEmpty();
|
2017-11-08 21:28:35 +01:00
|
|
|
|
2015-03-13 08:14:50 +01:00
|
|
|
/**
|
2017-11-08 21:28:35 +01:00
|
|
|
* Call abort() if a potential lock order deadlock bug is detected, instead of
|
|
|
|
* just logging information and throwing a logic_error. Defaults to true, and
|
|
|
|
* set to false in DEBUG_LOCKORDER unit tests.
|
|
|
|
*/
|
|
|
|
extern bool g_debug_lockorder_abort;
|
|
|
|
#else
|
2020-11-25 17:01:57 +01:00
|
|
|
template <typename MutexType>
|
|
|
|
inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false) {}
|
2020-06-22 20:12:14 +02:00
|
|
|
inline void LeaveCritical() {}
|
|
|
|
inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
|
2021-06-09 14:02:42 +02:00
|
|
|
template <typename MutexType>
|
Merge #19668: Do not hide compile-time thread safety warnings
ea74e10acf17903e44c85e3678853414653dd4e1 doc: Add best practice for annotating/asserting locks (Hennadii Stepanov)
2ee7743fe723227f2ea1b031eddb14fc6863f4c8 sync.h: Make runtime lock checks require compile-time lock checks (Anthony Towns)
23d71d171e6e22ba5e4a909d597a54595b2a2c1f Do not hide compile-time thread safety warnings (Hennadii Stepanov)
3ddc150857178bfb1c854c05bf9b526777876f56 Add missed thread safety annotations (Hennadii Stepanov)
af9ea55a72c94678b343f5dd98dc78f3a3ac58cb Use LockAssertion utility class instead of AssertLockHeld() (Hennadii Stepanov)
Pull request description:
On the way of transit from `RecursiveMutex` to `Mutex` (see #19303) it is crucial to have run-time `AssertLockHeld()` assertion that does _not_ hide compile-time Clang Thread Safety Analysis warnings.
On master (65e4ecabd5b4252154640c7bac38c92a3f3a7018) using `AssertLockHeld()` could hide Clang Thread Safety Analysis warnings, e.g., with the following patch applied:
```diff
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -607,7 +607,7 @@ public:
void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs);
- void removeForReorg(const CCoinsViewCache* pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main);
+ void removeForReorg(const CCoinsViewCache* pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs);
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight) EXCLUSIVE_LOCKS_REQUIRED(cs);
```
Clang compiles the code without any thread safety warnings.
See "Add missed thread safety annotations" commit for the actual thread safety warnings that are fixed in this PR.
ACKs for top commit:
MarcoFalke:
ACK ea74e10acf 🎙
jnewbery:
ACK ea74e10acf17903e44c85e3678853414653dd4e1
ajtowns:
ACK ea74e10acf17903e44c85e3678853414653dd4e1
Tree-SHA512: 8cba996e526751a1cb0e613c0cc1b10f027a3e9945fbfb4bd30f6355fd36b9f9c2e1e95ed3183fc254b42df7c30223278e18e5bdb5e1ef85db7fef067595d447
2020-09-01 08:18:20 +02:00
|
|
|
inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs) {}
|
|
|
|
template <typename MutexType>
|
2021-04-06 07:54:07 +02:00
|
|
|
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs) {}
|
2020-06-22 20:12:14 +02:00
|
|
|
inline void DeleteLock(void* cs) {}
|
|
|
|
inline bool LockStackEmpty() { return true; }
|
2017-11-08 21:28:35 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Template mixin that adds -Wthread-safety locking annotations and lock order
|
|
|
|
* checking to a subset of the mutex API.
|
2015-03-13 08:14:50 +01:00
|
|
|
*/
|
2012-11-11 03:50:26 +01:00
|
|
|
template <typename PARENT>
|
|
|
|
class LOCKABLE AnnotatedMixin : public PARENT
|
|
|
|
{
|
|
|
|
public:
|
2017-11-08 21:28:35 +01:00
|
|
|
~AnnotatedMixin() {
|
|
|
|
DeleteLock((void*)this);
|
|
|
|
}
|
|
|
|
|
2012-11-11 03:50:26 +01:00
|
|
|
void lock() EXCLUSIVE_LOCK_FUNCTION()
|
|
|
|
{
|
2014-09-19 19:21:46 +02:00
|
|
|
PARENT::lock();
|
2012-11-11 03:50:26 +01:00
|
|
|
}
|
2012-05-11 17:00:03 +02:00
|
|
|
|
2012-11-11 03:50:26 +01:00
|
|
|
void unlock() UNLOCK_FUNCTION()
|
|
|
|
{
|
2014-09-19 19:21:46 +02:00
|
|
|
PARENT::unlock();
|
2012-11-11 03:50:26 +01:00
|
|
|
}
|
2012-05-11 17:00:03 +02:00
|
|
|
|
2012-11-11 03:50:26 +01:00
|
|
|
bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true)
|
|
|
|
{
|
2014-09-19 19:21:46 +02:00
|
|
|
return PARENT::try_lock();
|
2012-11-11 03:50:26 +01:00
|
|
|
}
|
2012-05-11 17:00:03 +02:00
|
|
|
|
2017-11-08 21:28:35 +01:00
|
|
|
using UniqueLock = std::unique_lock<PARENT>;
|
2020-06-09 15:50:45 +02:00
|
|
|
#ifdef __clang__
|
|
|
|
//! For negative capabilities in the Clang Thread Safety Analysis.
|
|
|
|
//! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in conjunction
|
|
|
|
//! with the ! operator, to indicate that a mutex should not be held.
|
|
|
|
const AnnotatedMixin& operator!() const { return *this; }
|
|
|
|
#endif // __clang__
|
2017-11-08 21:28:35 +01:00
|
|
|
};
|
2012-05-11 17:00:03 +02:00
|
|
|
|
2024-03-20 20:38:58 +01:00
|
|
|
template <typename PARENT>
|
|
|
|
class LOCKABLE SharedAnnotatedMixin : public AnnotatedMixin<PARENT>
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
bool try_shared_lock() SHARED_TRYLOCK_FUNCTION(true)
|
|
|
|
{
|
|
|
|
return PARENT::try_shared_lock();
|
|
|
|
}
|
|
|
|
void shared_lock() SHARED_LOCK_FUNCTION()
|
|
|
|
{
|
|
|
|
PARENT::shared_lock();
|
|
|
|
}
|
|
|
|
using SharedLock = std::shared_lock<PARENT>;
|
|
|
|
};
|
|
|
|
|
2016-04-14 14:57:13 +02:00
|
|
|
/**
|
2017-11-07 19:29:21 +01:00
|
|
|
* Wrapped mutex: supports recursive locking, but no waiting
|
2016-04-14 14:57:13 +02:00
|
|
|
* TODO: We should move away from using the recursive lock by default.
|
|
|
|
*/
|
2019-01-15 19:41:59 +01:00
|
|
|
using RecursiveMutex = AnnotatedMixin<std::recursive_mutex>;
|
2016-04-14 14:57:13 +02:00
|
|
|
|
2017-11-07 19:29:21 +01:00
|
|
|
/** Wrapped mutex: supports waiting but not recursive locking */
|
2024-05-05 09:21:48 +02:00
|
|
|
using Mutex = AnnotatedMixin<std::mutex>;
|
|
|
|
|
2024-03-20 20:38:58 +01:00
|
|
|
/** Wrapped shared mutex: supports read locking via .shared_lock, exlusive locking via .lock;
|
|
|
|
* does not support recursive locking */
|
2024-05-05 09:21:48 +02:00
|
|
|
using SharedMutex = SharedAnnotatedMixin<std::shared_mutex>;
|
|
|
|
|
|
|
|
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
|
|
|
|
|
|
|
|
inline void AssertLockNotHeldInline(const char* name, const char* file, int line, Mutex* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) { AssertLockNotHeldInternal(name, file, line, cs); }
|
|
|
|
inline void AssertLockNotHeldInline(const char* name, const char* file, int line, RecursiveMutex* cs) LOCKS_EXCLUDED(cs) { AssertLockNotHeldInternal(name, file, line, cs); }
|
|
|
|
inline void AssertLockNotHeldInline(const char* name, const char* file, int line, SharedMutex* cs) LOCKS_EXCLUDED(cs) { AssertLockNotHeldInternal(name, file, line, cs); }
|
|
|
|
#define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs)
|
2017-11-07 19:29:21 +01:00
|
|
|
|
2017-11-08 21:28:35 +01:00
|
|
|
/** Wrapper around std::unique_lock style lock for Mutex. */
|
|
|
|
template <typename Mutex, typename Base = typename Mutex::UniqueLock>
|
2021-06-04 21:26:33 +02:00
|
|
|
class SCOPED_LOCKABLE UniqueLock : public Base
|
2012-05-11 17:00:03 +02:00
|
|
|
{
|
|
|
|
private:
|
|
|
|
void Enter(const char* pszName, const char* pszFile, int nLine)
|
|
|
|
{
|
2020-11-25 17:01:57 +01:00
|
|
|
EnterCritical(pszName, pszFile, nLine, Base::mutex());
|
Merge bitcoin/bitcoin#22736: log, sync: change lock contention from preprocessor directive to log category
7e698732836121912f179b7c743a72dd6fdffa72 sync: remove DEBUG_LOCKCONTENTION preprocessor directives (Jon Atack)
9b08006bc502e67956d6ab518388fad6397cac8d log, sync: improve lock contention logging and add time duration (Jon Atack)
3f4c6b87f1098436693c4990f2082515ec0ece26 log, timer: add timing macro in usec LOG_TIME_MICROS_WITH_CATEGORY (Jon Atack)
b7a17444e0746c562ae97b26eba431577947b06a log, sync: add LOCK logging category, apply it to lock contention (Jon Atack)
Pull request description:
To enable lock contention logging, `DEBUG_LOCKCONTENTION` has to be defined at compilation. Once built, the logging is not limited to a category and is high frequency, verbose and in all-caps. With these factors combined, it seems likely to be rarely used.
This patch:
- adds a `lock` logging category
- adds a timing macro in microseconds, `LOG_TIME_MICROS_WITH_CATEGORY`
- updates `BCLog::LogMsg()` to omit irrelevant decimals for microseconds and skip unneeded code and math
- improves the lock contention logging, drops the all-caps, and displays the duration in microseconds
- removes the conditional compilation directives
- allows lock contentions to be logged on startup with `-debug=lock` or at run time with `bitcoin-cli logging '["lock"]'`
```
$ bitcoind -signet -debug=lock
2021-09-01T12:40:01Z LockContention: cs_vNodes, net.cpp:1920 started
2021-09-01T12:40:01Z LockContention: cs_vNodes, net.cpp:1920 completed (4μs)
2021-09-01T12:40:01Z LockContention: cs_vNodes, net.cpp:1302 started
2021-09-01T12:40:01Z LockContention: cs_vNodes, net.cpp:1302 completed (4μs)
2021-09-01T12:40:02Z LockContention: cs_vNodes, net.cpp:2242 started
2021-09-01T12:40:02Z LockContention: cs_vNodes, net.cpp:2242 completed (20μs)
2021-09-01T12:43:04Z LockContention: ::cs_main, validation.cpp:4980 started
2021-09-01T12:43:04Z LockContention: ::cs_main, validation.cpp:4980 completed (3μs)
$ bitcoin-cli -signet logging
"lock": true,
$ bitcoin-cli -signet logging [] '["lock"]'
"lock": false,
$ bitcoin-cli -signet logging '["lock"]'
"lock": true,
```
I've tested this with Clang 13 and GCC 10.2.1, on Debian, with and without `--enable-debug`.
ACKs for top commit:
hebasto:
re-ACK 7e698732836121912f179b7c743a72dd6fdffa72, added a contention duration to the log message since my [previous](https://github.com/bitcoin/bitcoin/pull/22736#pullrequestreview-743764606) review.
theStack:
re-ACK 7e698732836121912f179b7c743a72dd6fdffa72 🔏 ⏲️
Tree-SHA512: c4b5eb88d3a2c051acaa842b3055ce30efde1f114f61da6e55fcaa27476c1c33a60bc419f7f5ccda532e1bdbe70815222ec2b2b6d9226f29c8e94e598aacfee7
2021-09-06 10:31:05 +02:00
|
|
|
if (Base::try_lock()) return;
|
2021-09-09 15:55:03 +02:00
|
|
|
LOG_TIME_MICROS_WITH_CATEGORY(strprintf("lock contention %s, %s:%d", pszName, pszFile, nLine), BCLog::LOCK);
|
Merge bitcoin/bitcoin#22736: log, sync: change lock contention from preprocessor directive to log category
7e698732836121912f179b7c743a72dd6fdffa72 sync: remove DEBUG_LOCKCONTENTION preprocessor directives (Jon Atack)
9b08006bc502e67956d6ab518388fad6397cac8d log, sync: improve lock contention logging and add time duration (Jon Atack)
3f4c6b87f1098436693c4990f2082515ec0ece26 log, timer: add timing macro in usec LOG_TIME_MICROS_WITH_CATEGORY (Jon Atack)
b7a17444e0746c562ae97b26eba431577947b06a log, sync: add LOCK logging category, apply it to lock contention (Jon Atack)
Pull request description:
To enable lock contention logging, `DEBUG_LOCKCONTENTION` has to be defined at compilation. Once built, the logging is not limited to a category and is high frequency, verbose and in all-caps. With these factors combined, it seems likely to be rarely used.
This patch:
- adds a `lock` logging category
- adds a timing macro in microseconds, `LOG_TIME_MICROS_WITH_CATEGORY`
- updates `BCLog::LogMsg()` to omit irrelevant decimals for microseconds and skip unneeded code and math
- improves the lock contention logging, drops the all-caps, and displays the duration in microseconds
- removes the conditional compilation directives
- allows lock contentions to be logged on startup with `-debug=lock` or at run time with `bitcoin-cli logging '["lock"]'`
```
$ bitcoind -signet -debug=lock
2021-09-01T12:40:01Z LockContention: cs_vNodes, net.cpp:1920 started
2021-09-01T12:40:01Z LockContention: cs_vNodes, net.cpp:1920 completed (4μs)
2021-09-01T12:40:01Z LockContention: cs_vNodes, net.cpp:1302 started
2021-09-01T12:40:01Z LockContention: cs_vNodes, net.cpp:1302 completed (4μs)
2021-09-01T12:40:02Z LockContention: cs_vNodes, net.cpp:2242 started
2021-09-01T12:40:02Z LockContention: cs_vNodes, net.cpp:2242 completed (20μs)
2021-09-01T12:43:04Z LockContention: ::cs_main, validation.cpp:4980 started
2021-09-01T12:43:04Z LockContention: ::cs_main, validation.cpp:4980 completed (3μs)
$ bitcoin-cli -signet logging
"lock": true,
$ bitcoin-cli -signet logging [] '["lock"]'
"lock": false,
$ bitcoin-cli -signet logging '["lock"]'
"lock": true,
```
I've tested this with Clang 13 and GCC 10.2.1, on Debian, with and without `--enable-debug`.
ACKs for top commit:
hebasto:
re-ACK 7e698732836121912f179b7c743a72dd6fdffa72, added a contention duration to the log message since my [previous](https://github.com/bitcoin/bitcoin/pull/22736#pullrequestreview-743764606) review.
theStack:
re-ACK 7e698732836121912f179b7c743a72dd6fdffa72 🔏 ⏲️
Tree-SHA512: c4b5eb88d3a2c051acaa842b3055ce30efde1f114f61da6e55fcaa27476c1c33a60bc419f7f5ccda532e1bdbe70815222ec2b2b6d9226f29c8e94e598aacfee7
2021-09-06 10:31:05 +02:00
|
|
|
Base::lock();
|
2012-05-11 17:00:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TryEnter(const char* pszName, const char* pszFile, int nLine)
|
|
|
|
{
|
2020-11-25 17:01:57 +01:00
|
|
|
EnterCritical(pszName, pszFile, nLine, Base::mutex(), true);
|
2022-10-03 19:17:00 +02:00
|
|
|
if (Base::try_lock()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
LeaveCritical();
|
|
|
|
return false;
|
2012-05-11 17:00:03 +02:00
|
|
|
}
|
|
|
|
|
2012-11-11 07:11:13 +01:00
|
|
|
public:
|
2021-06-04 21:26:33 +02:00
|
|
|
UniqueLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
|
2012-05-11 17:00:03 +02:00
|
|
|
{
|
|
|
|
if (fTry)
|
|
|
|
TryEnter(pszName, pszFile, nLine);
|
|
|
|
else
|
|
|
|
Enter(pszName, pszFile, nLine);
|
|
|
|
}
|
|
|
|
|
2021-06-04 21:26:33 +02:00
|
|
|
UniqueLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
|
2014-10-19 10:46:17 +02:00
|
|
|
{
|
|
|
|
if (!pmutexIn) return;
|
|
|
|
|
2017-11-08 21:28:35 +01:00
|
|
|
*static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock);
|
2014-10-19 10:46:17 +02:00
|
|
|
if (fTry)
|
|
|
|
TryEnter(pszName, pszFile, nLine);
|
|
|
|
else
|
|
|
|
Enter(pszName, pszFile, nLine);
|
|
|
|
}
|
|
|
|
|
2021-06-04 21:26:33 +02:00
|
|
|
~UniqueLock() UNLOCK_FUNCTION()
|
2012-05-11 17:00:03 +02:00
|
|
|
{
|
2017-11-08 21:28:35 +01:00
|
|
|
if (Base::owns_lock())
|
2012-05-11 17:00:03 +02:00
|
|
|
LeaveCritical();
|
|
|
|
}
|
|
|
|
|
|
|
|
operator bool()
|
|
|
|
{
|
2017-11-08 21:28:35 +01:00
|
|
|
return Base::owns_lock();
|
2012-05-11 17:00:03 +02:00
|
|
|
}
|
2020-03-06 20:47:49 +01:00
|
|
|
|
|
|
|
protected:
|
|
|
|
// needed for reverse_lock
|
|
|
|
UniqueLock() { }
|
|
|
|
|
|
|
|
public:
|
|
|
|
/**
|
|
|
|
* An RAII-style reverse lock. Unlocks on construction and locks on destruction.
|
|
|
|
*/
|
|
|
|
class reverse_lock {
|
|
|
|
public:
|
|
|
|
explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
|
|
|
|
CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
|
|
|
|
lock.unlock();
|
|
|
|
LeaveCritical();
|
|
|
|
lock.swap(templock);
|
|
|
|
}
|
|
|
|
|
|
|
|
~reverse_lock() {
|
|
|
|
templock.swap(lock);
|
2020-11-25 17:01:57 +01:00
|
|
|
EnterCritical(lockname.c_str(), file.c_str(), line, lock.mutex());
|
2020-03-06 20:47:49 +01:00
|
|
|
lock.lock();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
reverse_lock(reverse_lock const&);
|
|
|
|
reverse_lock& operator=(reverse_lock const&);
|
|
|
|
|
|
|
|
UniqueLock& lock;
|
|
|
|
UniqueLock templock;
|
|
|
|
std::string lockname;
|
|
|
|
const std::string file;
|
|
|
|
const int line;
|
|
|
|
};
|
|
|
|
friend class reverse_lock;
|
2012-05-11 17:00:03 +02:00
|
|
|
};
|
|
|
|
|
2024-03-20 20:38:58 +01:00
|
|
|
template <typename Mutex, typename Base = typename Mutex::SharedLock>
|
|
|
|
class SCOPED_LOCKABLE SharedLock : public Base
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
void SharedEnter(const char* pszName, const char* pszFile, int nLine)
|
|
|
|
{
|
|
|
|
EnterCritical(pszName, pszFile, nLine, Base::mutex());
|
|
|
|
#ifdef DEBUG_LOCKCONTENTION
|
|
|
|
if (!Base::try_lock()) {
|
|
|
|
PrintLockContention(pszName, pszFile, nLine);
|
|
|
|
#endif
|
|
|
|
Base::lock();
|
|
|
|
#ifdef DEBUG_LOCKCONTENTION
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TrySharedEnter(const char* pszName, const char* pszFile, int nLine)
|
|
|
|
{
|
|
|
|
EnterCritical(pszName, pszFile, nLine, Base::mutex(), true);
|
|
|
|
if (Base::try_lock()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
LeaveCritical();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
SharedLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) SHARED_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
|
|
|
|
{
|
|
|
|
if (fTry) {
|
|
|
|
TrySharedEnter(pszName, pszFile, nLine);
|
|
|
|
} else {
|
|
|
|
SharedEnter(pszName, pszFile, nLine);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SharedLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) SHARED_LOCK_FUNCTION(pmutexIn)
|
|
|
|
{
|
|
|
|
if (!pmutexIn) return;
|
|
|
|
|
|
|
|
*static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock);
|
|
|
|
if (fTry) {
|
|
|
|
TrySharedEnter(pszName, pszFile, nLine);
|
|
|
|
} else {
|
|
|
|
SharedEnter(pszName, pszFile, nLine);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~SharedLock() UNLOCK_FUNCTION()
|
|
|
|
{
|
|
|
|
if (Base::owns_lock()) {
|
|
|
|
LeaveCritical();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-04-13 22:32:45 +02:00
|
|
|
#define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, #g, __FILE__, __LINE__)
|
2020-03-06 20:47:49 +01:00
|
|
|
|
2017-11-08 21:28:35 +01:00
|
|
|
template<typename MutexArg>
|
2021-06-04 21:26:33 +02:00
|
|
|
using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>;
|
2024-03-20 20:38:58 +01:00
|
|
|
template<typename MutexArg>
|
|
|
|
using ReadLock = SharedLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>;
|
2017-11-08 21:28:35 +01:00
|
|
|
|
2022-04-13 22:32:45 +02:00
|
|
|
#define LOCK(cs) DebugLock<decltype(cs)> UNIQUE_NAME(criticalblock)(cs, #cs, __FILE__, __LINE__)
|
|
|
|
#define READ_LOCK(cs) ReadLock<decltype(cs)> UNIQUE_NAME(criticalblock)(cs, #cs, __FILE__, __LINE__)
|
2017-11-08 21:28:35 +01:00
|
|
|
#define LOCK2(cs1, cs2) \
|
|
|
|
DebugLock<decltype(cs1)> criticalblock1(cs1, #cs1, __FILE__, __LINE__); \
|
|
|
|
DebugLock<decltype(cs2)> criticalblock2(cs2, #cs2, __FILE__, __LINE__);
|
|
|
|
#define TRY_LOCK(cs, name) DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__, true)
|
2024-03-20 20:38:58 +01:00
|
|
|
#define TRY_READ_LOCK(cs, name) ReadLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__, true)
|
2017-11-08 21:28:35 +01:00
|
|
|
#define WAIT_LOCK(cs, name) DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__)
|
2012-05-11 17:00:03 +02:00
|
|
|
|
2014-09-19 19:21:46 +02:00
|
|
|
#define ENTER_CRITICAL_SECTION(cs) \
|
|
|
|
{ \
|
2020-11-25 17:01:57 +01:00
|
|
|
EnterCritical(#cs, __FILE__, __LINE__, &cs); \
|
2014-09-19 19:21:46 +02:00
|
|
|
(cs).lock(); \
|
2012-05-11 17:00:03 +02:00
|
|
|
}
|
|
|
|
|
2020-12-11 01:54:57 +01:00
|
|
|
#define LEAVE_CRITICAL_SECTION(cs) \
|
|
|
|
{ \
|
|
|
|
std::string lockname; \
|
2024-02-23 11:23:42 +01:00
|
|
|
CheckLastCritical(reinterpret_cast<void*>(&cs), lockname, #cs, __FILE__, __LINE__); \
|
2020-12-11 01:54:57 +01:00
|
|
|
(cs).unlock(); \
|
|
|
|
LeaveCritical(); \
|
2012-05-11 17:00:03 +02:00
|
|
|
}
|
|
|
|
|
2019-04-19 13:48:31 +02:00
|
|
|
//! Run code while locking a mutex.
|
|
|
|
//!
|
|
|
|
//! Examples:
|
|
|
|
//!
|
|
|
|
//! WITH_LOCK(cs, shared_val = shared_val + 1);
|
|
|
|
//!
|
|
|
|
//! int val = WITH_LOCK(cs, return shared_val);
|
|
|
|
//!
|
2021-01-12 08:30:38 +01:00
|
|
|
//! Note:
|
|
|
|
//!
|
|
|
|
//! Since the return type deduction follows that of decltype(auto), while the
|
|
|
|
//! deduced type of:
|
|
|
|
//!
|
|
|
|
//! WITH_LOCK(cs, return {int i = 1; return i;});
|
|
|
|
//!
|
|
|
|
//! is int, the deduced type of:
|
|
|
|
//!
|
|
|
|
//! WITH_LOCK(cs, return {int j = 1; return (j);});
|
|
|
|
//!
|
|
|
|
//! is &int, a reference to a local variable
|
|
|
|
//!
|
|
|
|
//! The above is detectable at compile-time with the -Wreturn-local-addr flag in
|
|
|
|
//! gcc and the -Wreturn-stack-address flag in clang, both enabled by default.
|
|
|
|
#define WITH_LOCK(cs, code) [&]() -> decltype(auto) { LOCK(cs); code; }()
|
2024-03-20 20:38:58 +01:00
|
|
|
#define WITH_READ_LOCK(cs, code) [&]() -> decltype(auto) { READ_LOCK(cs); code; }()
|
2019-04-19 13:48:31 +02:00
|
|
|
|
2024-10-09 14:20:57 +02:00
|
|
|
/** An implementation of a semaphore.
|
|
|
|
*
|
|
|
|
* See https://en.wikipedia.org/wiki/Semaphore_(programming)
|
|
|
|
*/
|
2012-05-11 17:00:03 +02:00
|
|
|
class CSemaphore
|
|
|
|
{
|
|
|
|
private:
|
2017-11-28 08:37:50 +01:00
|
|
|
std::condition_variable condition;
|
|
|
|
std::mutex mutex;
|
2012-05-13 17:55:23 +02:00
|
|
|
int value;
|
2012-05-11 17:00:03 +02:00
|
|
|
|
|
|
|
public:
|
2024-10-09 14:20:57 +02:00
|
|
|
explicit CSemaphore(int init) noexcept : value(init) {}
|
|
|
|
|
|
|
|
// Disallow default construct, copy, move.
|
|
|
|
CSemaphore() = delete;
|
|
|
|
CSemaphore(const CSemaphore&) = delete;
|
|
|
|
CSemaphore(CSemaphore&&) = delete;
|
|
|
|
CSemaphore& operator=(const CSemaphore&) = delete;
|
|
|
|
CSemaphore& operator=(CSemaphore&&) = delete;
|
2012-05-11 17:00:03 +02:00
|
|
|
|
2024-10-09 14:20:57 +02:00
|
|
|
void wait() noexcept
|
2014-09-19 19:21:46 +02:00
|
|
|
{
|
2017-11-28 08:37:50 +01:00
|
|
|
std::unique_lock<std::mutex> lock(mutex);
|
|
|
|
condition.wait(lock, [&]() { return value >= 1; });
|
2012-05-13 17:55:23 +02:00
|
|
|
value--;
|
2012-05-11 17:00:03 +02:00
|
|
|
}
|
|
|
|
|
2024-10-09 14:20:57 +02:00
|
|
|
bool try_wait() noexcept
|
2014-09-19 19:21:46 +02:00
|
|
|
{
|
2017-11-28 08:37:50 +01:00
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
2024-10-09 14:20:57 +02:00
|
|
|
if (value < 1) {
|
2012-05-13 17:55:23 +02:00
|
|
|
return false;
|
2024-10-09 14:20:57 +02:00
|
|
|
}
|
2012-05-13 17:55:23 +02:00
|
|
|
value--;
|
|
|
|
return true;
|
2012-05-11 17:00:03 +02:00
|
|
|
}
|
|
|
|
|
2024-10-09 14:20:57 +02:00
|
|
|
void post() noexcept
|
2014-09-19 19:21:46 +02:00
|
|
|
{
|
2012-05-13 17:55:23 +02:00
|
|
|
{
|
2017-11-28 08:37:50 +01:00
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
2012-05-13 17:55:23 +02:00
|
|
|
value++;
|
|
|
|
}
|
|
|
|
condition.notify_one();
|
2012-05-11 17:00:03 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/** RAII-style semaphore lock */
|
|
|
|
class CSemaphoreGrant
|
|
|
|
{
|
|
|
|
private:
|
2014-09-19 19:21:46 +02:00
|
|
|
CSemaphore* sem;
|
2012-05-11 17:00:03 +02:00
|
|
|
bool fHaveGrant;
|
|
|
|
|
|
|
|
public:
|
2024-10-09 14:20:57 +02:00
|
|
|
void Acquire() noexcept
|
2014-09-19 19:21:46 +02:00
|
|
|
{
|
2024-10-09 14:20:57 +02:00
|
|
|
if (fHaveGrant) {
|
2012-05-11 17:00:03 +02:00
|
|
|
return;
|
2024-10-09 14:20:57 +02:00
|
|
|
}
|
2012-05-11 17:00:03 +02:00
|
|
|
sem->wait();
|
|
|
|
fHaveGrant = true;
|
|
|
|
}
|
|
|
|
|
2024-10-09 14:20:57 +02:00
|
|
|
void Release() noexcept
|
2014-09-19 19:21:46 +02:00
|
|
|
{
|
2024-10-09 14:20:57 +02:00
|
|
|
if (!fHaveGrant) {
|
2012-05-11 17:00:03 +02:00
|
|
|
return;
|
2024-10-09 14:20:57 +02:00
|
|
|
}
|
2012-05-11 17:00:03 +02:00
|
|
|
sem->post();
|
|
|
|
fHaveGrant = false;
|
|
|
|
}
|
|
|
|
|
2024-10-09 14:20:57 +02:00
|
|
|
bool TryAcquire() noexcept
|
2014-09-19 19:21:46 +02:00
|
|
|
{
|
2024-10-09 14:20:57 +02:00
|
|
|
if (!fHaveGrant && sem->try_wait()) {
|
2012-05-11 17:00:03 +02:00
|
|
|
fHaveGrant = true;
|
2024-10-09 14:20:57 +02:00
|
|
|
}
|
2012-05-11 17:00:03 +02:00
|
|
|
return fHaveGrant;
|
|
|
|
}
|
|
|
|
|
2024-10-09 14:20:57 +02:00
|
|
|
// Disallow copy.
|
|
|
|
CSemaphoreGrant(const CSemaphoreGrant&) = delete;
|
|
|
|
CSemaphoreGrant& operator=(const CSemaphoreGrant&) = delete;
|
|
|
|
|
|
|
|
// Allow move.
|
|
|
|
CSemaphoreGrant(CSemaphoreGrant&& other) noexcept
|
2014-09-19 19:21:46 +02:00
|
|
|
{
|
2024-10-09 14:20:57 +02:00
|
|
|
sem = other.sem;
|
|
|
|
fHaveGrant = other.fHaveGrant;
|
|
|
|
other.fHaveGrant = false;
|
|
|
|
other.sem = nullptr;
|
2012-05-11 17:00:03 +02:00
|
|
|
}
|
|
|
|
|
2024-10-09 14:20:57 +02:00
|
|
|
CSemaphoreGrant& operator=(CSemaphoreGrant&& other) noexcept
|
|
|
|
{
|
|
|
|
Release();
|
|
|
|
sem = other.sem;
|
|
|
|
fHaveGrant = other.fHaveGrant;
|
|
|
|
other.fHaveGrant = false;
|
|
|
|
other.sem = nullptr;
|
|
|
|
return *this;
|
|
|
|
}
|
2012-05-11 17:00:03 +02:00
|
|
|
|
2024-10-09 14:20:57 +02:00
|
|
|
CSemaphoreGrant() noexcept : sem(nullptr), fHaveGrant(false) {}
|
|
|
|
|
|
|
|
explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) noexcept : sem(&sema), fHaveGrant(false)
|
2014-09-19 19:21:46 +02:00
|
|
|
{
|
2024-10-09 14:20:57 +02:00
|
|
|
if (fTry) {
|
2012-05-11 17:00:03 +02:00
|
|
|
TryAcquire();
|
2024-10-09 14:20:57 +02:00
|
|
|
} else {
|
2012-05-11 17:00:03 +02:00
|
|
|
Acquire();
|
2024-10-09 14:20:57 +02:00
|
|
|
}
|
2012-05-11 17:00:03 +02:00
|
|
|
}
|
|
|
|
|
2014-09-19 19:21:46 +02:00
|
|
|
~CSemaphoreGrant()
|
|
|
|
{
|
2012-05-11 17:00:03 +02:00
|
|
|
Release();
|
|
|
|
}
|
|
|
|
|
2024-10-09 14:20:57 +02:00
|
|
|
explicit operator bool() const noexcept
|
2014-09-19 19:21:46 +02:00
|
|
|
{
|
2012-05-11 17:00:03 +02:00
|
|
|
return fHaveGrant;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-08-28 22:21:03 +02:00
|
|
|
#endif // BITCOIN_SYNC_H
|