mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
Merge pull request #4190 from kittywhiskers/tlocks
merge #11640, #11599, #16112, #16127, #18635, #19249: thread safety and locking improvements
This commit is contained in:
commit
a8aee57447
@ -362,7 +362,7 @@ if test "x$enable_werror" = "xyes"; then
|
||||
AC_MSG_ERROR("enable-werror set but -Werror is not usable")
|
||||
fi
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=vla],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=vla"],,[[$CXXFLAG_WERROR]])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=thread-safety-analysis],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=thread-safety-analysis"],,[[$CXXFLAG_WERROR]])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=thread-safety],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=thread-safety"],,[[$CXXFLAG_WERROR]])
|
||||
fi
|
||||
|
||||
if test "x$CXXFLAGS_overridden" = "xno"; then
|
||||
@ -371,7 +371,7 @@ if test "x$CXXFLAGS_overridden" = "xno"; then
|
||||
AX_CHECK_COMPILE_FLAG([-Wformat],[CXXFLAGS="$CXXFLAGS -Wformat"],,[[$CXXFLAG_WERROR]])
|
||||
AX_CHECK_COMPILE_FLAG([-Wvla],[CXXFLAGS="$CXXFLAGS -Wvla"],,[[$CXXFLAG_WERROR]])
|
||||
AX_CHECK_COMPILE_FLAG([-Wformat-security],[CXXFLAGS="$CXXFLAGS -Wformat-security"],,[[$CXXFLAG_WERROR]])
|
||||
AX_CHECK_COMPILE_FLAG([-Wthread-safety-analysis],[CXXFLAGS="$CXXFLAGS -Wthread-safety-analysis"],,[[$CXXFLAG_WERROR]])
|
||||
AX_CHECK_COMPILE_FLAG([-Wthread-safety],[CXXFLAGS="$CXXFLAGS -Wthread-safety"],,[[$CXXFLAG_WERROR]])
|
||||
AX_CHECK_COMPILE_FLAG([-Wrange-loop-analysis],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wrange-loop-analysis"],,[[$CXXFLAG_WERROR]])
|
||||
|
||||
## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all
|
||||
|
@ -101,6 +101,7 @@ BITCOIN_TESTS =\
|
||||
test/skiplist_tests.cpp \
|
||||
test/streams_tests.cpp \
|
||||
test/subsidy_tests.cpp \
|
||||
test/sync_tests.cpp \
|
||||
test/timedata_tests.cpp \
|
||||
test/torcontrol_tests.cpp \
|
||||
test/transaction_tests.cpp \
|
||||
|
@ -106,13 +106,12 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
|
||||
std::vector<bool> have_txn(txn_available.size());
|
||||
{
|
||||
LOCK(pool->cs);
|
||||
const std::vector<std::pair<uint256, CTxMemPool::txiter> >& vTxHashes = pool->vTxHashes;
|
||||
for (size_t i = 0; i < vTxHashes.size(); i++) {
|
||||
uint64_t shortid = cmpctblock.GetShortID(vTxHashes[i].first);
|
||||
for (size_t i = 0; i < pool->vTxHashes.size(); i++) {
|
||||
uint64_t shortid = cmpctblock.GetShortID(pool->vTxHashes[i].first);
|
||||
std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);
|
||||
if (idit != shorttxids.end()) {
|
||||
if (!have_txn[idit->second]) {
|
||||
txn_available[idit->second] = vTxHashes[i].second->GetSharedTx();
|
||||
txn_available[idit->second] = pool->vTxHashes[i].second->GetSharedTx();
|
||||
have_txn[idit->second] = true;
|
||||
mempool_count++;
|
||||
} else {
|
||||
|
@ -127,7 +127,7 @@ class PartiallyDownloadedBlock {
|
||||
protected:
|
||||
std::vector<CTransactionRef> txn_available;
|
||||
size_t prefilled_count = 0, mempool_count = 0, extra_count = 0;
|
||||
CTxMemPool* pool;
|
||||
const CTxMemPool* pool;
|
||||
public:
|
||||
CBlockHeader header;
|
||||
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
|
||||
|
@ -74,7 +74,7 @@ class WorkQueue
|
||||
{
|
||||
private:
|
||||
/** Mutex protects entire object */
|
||||
std::mutex cs;
|
||||
Mutex cs;
|
||||
std::condition_variable cond;
|
||||
std::deque<std::unique_ptr<WorkItem>> queue;
|
||||
bool running;
|
||||
@ -93,7 +93,7 @@ public:
|
||||
/** Enqueue a work item */
|
||||
bool Enqueue(WorkItem* item)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(cs);
|
||||
LOCK(cs);
|
||||
if (queue.size() >= maxDepth) {
|
||||
return false;
|
||||
}
|
||||
@ -107,7 +107,7 @@ public:
|
||||
while (true) {
|
||||
std::unique_ptr<WorkItem> i;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(cs);
|
||||
WAIT_LOCK(cs, lock);
|
||||
while (running && queue.empty())
|
||||
cond.wait(lock);
|
||||
if (!running)
|
||||
@ -121,7 +121,7 @@ public:
|
||||
/** Interrupt and exit loops */
|
||||
void Interrupt()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(cs);
|
||||
LOCK(cs);
|
||||
running = false;
|
||||
cond.notify_all();
|
||||
}
|
||||
|
26
src/init.cpp
26
src/init.cpp
@ -702,17 +702,17 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex
|
||||
}
|
||||
|
||||
static bool fHaveGenesis = false;
|
||||
static CWaitableCriticalSection cs_GenesisWait;
|
||||
static CConditionVariable condvar_GenesisWait;
|
||||
static Mutex g_genesis_wait_mutex;
|
||||
static std::condition_variable g_genesis_wait_cv;
|
||||
|
||||
static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex)
|
||||
{
|
||||
if (pBlockIndex != nullptr) {
|
||||
{
|
||||
WaitableLock lock_GenesisWait(cs_GenesisWait);
|
||||
LOCK(g_genesis_wait_mutex);
|
||||
fHaveGenesis = true;
|
||||
}
|
||||
condvar_GenesisWait.notify_all();
|
||||
g_genesis_wait_cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1089,12 +1089,6 @@ void InitLogging()
|
||||
{
|
||||
g_logger->m_print_to_file = !gArgs.IsArgNegated("-debuglogfile");
|
||||
g_logger->m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE));
|
||||
|
||||
// Add newlines to the logfile to distinguish this execution from the last
|
||||
// one; called before console logging is set up, so this is only sent to
|
||||
// debug.log.
|
||||
LogPrintf("\n\n\n\n\n");
|
||||
|
||||
g_logger->m_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
|
||||
g_logger->m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
|
||||
g_logger->m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
|
||||
@ -1709,10 +1703,10 @@ bool AppInitMain()
|
||||
// and because this needs to happen before any other debug.log printing
|
||||
g_logger->ShrinkDebugFile();
|
||||
}
|
||||
if (!g_logger->OpenDebugLog()) {
|
||||
return InitError(strprintf("Could not open debug log file %s",
|
||||
g_logger->m_file_path.string()));
|
||||
}
|
||||
}
|
||||
if (!g_logger->StartLogging()) {
|
||||
return InitError(strprintf("Could not open debug log file %s",
|
||||
g_logger->m_file_path.string()));
|
||||
}
|
||||
|
||||
if (!g_logger->m_log_timestamps)
|
||||
@ -2377,12 +2371,12 @@ bool AppInitMain()
|
||||
|
||||
// Wait for genesis block to be processed
|
||||
{
|
||||
WaitableLock lock(cs_GenesisWait);
|
||||
WAIT_LOCK(g_genesis_wait_mutex, lock);
|
||||
// We previously could hang here if StartShutdown() is called prior to
|
||||
// ThreadImport getting started, so instead we just wait on a timer to
|
||||
// check ShutdownRequested() regularly.
|
||||
while (!fHaveGenesis && !ShutdownRequested()) {
|
||||
condvar_GenesisWait.wait_for(lock, std::chrono::milliseconds(500));
|
||||
g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500));
|
||||
}
|
||||
uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait);
|
||||
}
|
||||
|
@ -31,24 +31,38 @@ static int FileWriteStr(const std::string &str, FILE *fp)
|
||||
return fwrite(str.data(), 1, str.size(), fp);
|
||||
}
|
||||
|
||||
bool BCLog::Logger::OpenDebugLog()
|
||||
bool BCLog::Logger::StartLogging()
|
||||
{
|
||||
std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
|
||||
assert(m_buffering);
|
||||
assert(m_fileout == nullptr);
|
||||
assert(!m_file_path.empty());
|
||||
|
||||
m_fileout = fsbridge::fopen(m_file_path, "a");
|
||||
if (!m_fileout) {
|
||||
return false;
|
||||
if (m_print_to_file) {
|
||||
assert(!m_file_path.empty());
|
||||
m_fileout = fsbridge::fopen(m_file_path, "a");
|
||||
if (!m_fileout) {
|
||||
return false;
|
||||
}
|
||||
|
||||
setbuf(m_fileout, nullptr); // unbuffered
|
||||
|
||||
// Add newlines to the logfile to distinguish this execution from the
|
||||
// last one.
|
||||
FileWriteStr("\n\n\n\n\n", m_fileout);
|
||||
}
|
||||
|
||||
setbuf(m_fileout, nullptr); // unbuffered
|
||||
// dump buffered messages from before we opened the log
|
||||
m_buffering = false;
|
||||
while (!m_msgs_before_open.empty()) {
|
||||
FileWriteStr(m_msgs_before_open.front(), m_fileout);
|
||||
const std::string& s = m_msgs_before_open.front();
|
||||
|
||||
if (m_print_to_file) FileWriteStr(s, m_fileout);
|
||||
if (m_print_to_console) fwrite(s.data(), 1, s.size(), stdout);
|
||||
|
||||
m_msgs_before_open.pop_front();
|
||||
}
|
||||
if (m_print_to_console) fflush(stdout);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -247,8 +261,9 @@ std::string BCLog::Logger::LogThreadNameStr(const std::string &str)
|
||||
return strThreadLogged;
|
||||
}
|
||||
|
||||
void BCLog::Logger::LogPrintStr(const std::string &str)
|
||||
void BCLog::Logger::LogPrintStr(const std::string& str)
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
std::string strThreadLogged = LogThreadNameStr(str);
|
||||
std::string strTimestamped = LogTimestampStr(strThreadLogged);
|
||||
|
||||
@ -257,32 +272,31 @@ void BCLog::Logger::LogPrintStr(const std::string &str)
|
||||
else
|
||||
m_started_new_line = false;
|
||||
|
||||
if (m_buffering) {
|
||||
// buffer if we haven't started logging yet
|
||||
m_msgs_before_open.push_back(strTimestamped);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_print_to_console) {
|
||||
// print to console
|
||||
fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
if (m_print_to_file) {
|
||||
std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
|
||||
assert(m_fileout != nullptr);
|
||||
|
||||
// buffer if we haven't opened the log yet
|
||||
if (m_fileout == nullptr) {
|
||||
m_msgs_before_open.push_back(strTimestamped);
|
||||
}
|
||||
else
|
||||
{
|
||||
// reopen the log file, if requested
|
||||
if (m_reopen_file) {
|
||||
m_reopen_file = false;
|
||||
m_fileout = fsbridge::freopen(m_file_path, "a", m_fileout);
|
||||
if (!m_fileout) {
|
||||
return;
|
||||
}
|
||||
setbuf(m_fileout, nullptr); // unbuffered
|
||||
// reopen the log file, if requested
|
||||
if (m_reopen_file) {
|
||||
m_reopen_file = false;
|
||||
FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
|
||||
if (new_fileout) {
|
||||
setbuf(new_fileout, nullptr); // unbuffered
|
||||
fclose(m_fileout);
|
||||
m_fileout = new_fileout;
|
||||
}
|
||||
|
||||
FileWriteStr(strTimestamped, m_fileout);
|
||||
}
|
||||
FileWriteStr(strTimestamped, m_fileout);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include <fs.h>
|
||||
#include <tinyformat.h>
|
||||
#include <threadsafety.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
@ -82,9 +83,11 @@ namespace BCLog {
|
||||
class Logger
|
||||
{
|
||||
private:
|
||||
FILE* m_fileout = nullptr;
|
||||
std::mutex m_file_mutex;
|
||||
std::list<std::string> m_msgs_before_open;
|
||||
mutable StdMutex m_cs; // Can not use Mutex from sync.h because in debug mode it would cause a deadlock when a potential deadlock was detected
|
||||
|
||||
FILE* m_fileout GUARDED_BY(m_cs) = nullptr;
|
||||
std::list<std::string> m_msgs_before_open GUARDED_BY(m_cs);
|
||||
bool m_buffering GUARDED_BY(m_cs) = true; //!< Buffer messages before logging can be started.
|
||||
|
||||
/**
|
||||
* m_started_new_line is a state variable that will suppress printing of
|
||||
@ -111,12 +114,18 @@ namespace BCLog {
|
||||
std::atomic<bool> m_reopen_file{false};
|
||||
|
||||
/** Send a string to the log output */
|
||||
void LogPrintStr(const std::string &str);
|
||||
void LogPrintStr(const std::string& str);
|
||||
|
||||
/** Returns whether logs will be written to any output */
|
||||
bool Enabled() const { return m_print_to_console || m_print_to_file; }
|
||||
bool Enabled() const
|
||||
{
|
||||
StdLockGuard scoped_lock(m_cs);
|
||||
return m_buffering || m_print_to_console || m_print_to_file;
|
||||
}
|
||||
|
||||
/** Start logging (and flush all buffered messages) */
|
||||
bool StartLogging();
|
||||
|
||||
bool OpenDebugLog();
|
||||
void ShrinkDebugFile();
|
||||
|
||||
uint64_t GetCategoryMask() const { return m_categories.load(); }
|
||||
|
10
src/net.cpp
10
src/net.cpp
@ -2040,7 +2040,7 @@ void CConnman::ThreadSocketHandler()
|
||||
void CConnman::WakeMessageHandler()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutexMsgProc);
|
||||
LOCK(mutexMsgProc);
|
||||
fMsgProcWake = true;
|
||||
}
|
||||
condMsgProc.notify_one();
|
||||
@ -2862,9 +2862,9 @@ void CConnman::ThreadMessageHandler()
|
||||
|
||||
ReleaseNodeVector(vNodesCopy);
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutexMsgProc);
|
||||
WAIT_LOCK(mutexMsgProc, lock);
|
||||
if (!fMoreWork) {
|
||||
condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return fMsgProcWake; });
|
||||
condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this]() EXCLUSIVE_LOCKS_REQUIRED(mutexMsgProc) { return fMsgProcWake; });
|
||||
}
|
||||
fMsgProcWake = false;
|
||||
}
|
||||
@ -3198,7 +3198,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
|
||||
flagInterruptMsgProc = false;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutexMsgProc);
|
||||
LOCK(mutexMsgProc);
|
||||
fMsgProcWake = false;
|
||||
}
|
||||
|
||||
@ -3303,7 +3303,7 @@ void CExplicitNetCleanup::callCleanup()
|
||||
void CConnman::Interrupt()
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutexMsgProc);
|
||||
LOCK(mutexMsgProc);
|
||||
flagInterruptMsgProc = true;
|
||||
}
|
||||
condMsgProc.notify_all();
|
||||
|
@ -617,10 +617,10 @@ private:
|
||||
const uint64_t nSeed0, nSeed1;
|
||||
|
||||
/** flag for waking the message processor. */
|
||||
bool fMsgProcWake;
|
||||
bool fMsgProcWake GUARDED_BY(mutexMsgProc);
|
||||
|
||||
std::condition_variable condMsgProc;
|
||||
std::mutex mutexMsgProc;
|
||||
Mutex mutexMsgProc;
|
||||
std::atomic<bool> flagInterruptMsgProc;
|
||||
|
||||
CThreadInterrupt interruptNet;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <wincrypt.h>
|
||||
#endif
|
||||
#include <logging.h> // for LogPrint()
|
||||
#include <sync.h> // for WAIT_LOCK
|
||||
#include <utiltime.h> // for GetTime()
|
||||
|
||||
#include <stdlib.h>
|
||||
@ -295,7 +296,7 @@ void RandAddSeedSleep()
|
||||
}
|
||||
|
||||
|
||||
static std::mutex cs_rng_state;
|
||||
static Mutex cs_rng_state;
|
||||
static unsigned char rng_state[32] = {0};
|
||||
static uint64_t rng_counter = 0;
|
||||
|
||||
@ -305,7 +306,7 @@ static void AddDataToRng(void* data, size_t len) {
|
||||
hasher.Write((const unsigned char*)data, len);
|
||||
unsigned char buf[64];
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(cs_rng_state);
|
||||
WAIT_LOCK(cs_rng_state, lock);
|
||||
hasher.Write(rng_state, sizeof(rng_state));
|
||||
hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
|
||||
++rng_counter;
|
||||
@ -337,7 +338,7 @@ void GetStrongRandBytes(unsigned char* out, int num)
|
||||
|
||||
// Combine with and update state
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(cs_rng_state);
|
||||
WAIT_LOCK(cs_rng_state, lock);
|
||||
hasher.Write(rng_state, sizeof(rng_state));
|
||||
hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
|
||||
++rng_counter;
|
||||
|
@ -53,9 +53,9 @@ struct CUpdatedBlock
|
||||
int height;
|
||||
};
|
||||
|
||||
static std::mutex cs_blockchange;
|
||||
static Mutex cs_blockchange;
|
||||
static std::condition_variable cond_blockchange;
|
||||
static CUpdatedBlock latestblock;
|
||||
static CUpdatedBlock latestblock GUARDED_BY(cs_blockchange);
|
||||
|
||||
extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry);
|
||||
|
||||
@ -247,7 +247,7 @@ static UniValue getbestchainlock(const JSONRPCRequest& request)
|
||||
void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
|
||||
{
|
||||
if(pindex) {
|
||||
std::lock_guard<std::mutex> lock(cs_blockchange);
|
||||
LOCK(cs_blockchange);
|
||||
latestblock.hash = pindex->GetBlockHash();
|
||||
latestblock.height = pindex->nHeight;
|
||||
}
|
||||
@ -278,12 +278,12 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
|
||||
|
||||
CUpdatedBlock block;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(cs_blockchange);
|
||||
WAIT_LOCK(cs_blockchange, lock);
|
||||
block = latestblock;
|
||||
if(timeout)
|
||||
cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
|
||||
cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
|
||||
else
|
||||
cond_blockchange.wait(lock, [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
|
||||
cond_blockchange.wait(lock, [&block]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); });
|
||||
block = latestblock;
|
||||
}
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
@ -320,11 +320,11 @@ static UniValue waitforblock(const JSONRPCRequest& request)
|
||||
|
||||
CUpdatedBlock block;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(cs_blockchange);
|
||||
WAIT_LOCK(cs_blockchange, lock);
|
||||
if(timeout)
|
||||
cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]{return latestblock.hash == hash || !IsRPCRunning();});
|
||||
cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.hash == hash || !IsRPCRunning();});
|
||||
else
|
||||
cond_blockchange.wait(lock, [&hash]{return latestblock.hash == hash || !IsRPCRunning(); });
|
||||
cond_blockchange.wait(lock, [&hash]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.hash == hash || !IsRPCRunning(); });
|
||||
block = latestblock;
|
||||
}
|
||||
|
||||
@ -363,13 +363,14 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
|
||||
|
||||
CUpdatedBlock block;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(cs_blockchange);
|
||||
WAIT_LOCK(cs_blockchange, lock);
|
||||
if(timeout)
|
||||
cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]{return latestblock.height >= height || !IsRPCRunning();});
|
||||
cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning();});
|
||||
else
|
||||
cond_blockchange.wait(lock, [&height]{return latestblock.height >= height || !IsRPCRunning(); });
|
||||
cond_blockchange.wait(lock, [&height]() EXCLUSIVE_LOCKS_REQUIRED(cs_blockchange) {return latestblock.height >= height || !IsRPCRunning(); });
|
||||
block = latestblock;
|
||||
}
|
||||
// TODO: Backport g_utxosetscan and associated logic from #16127
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.pushKV("hash", block.hash.GetHex());
|
||||
ret.pushKV("height", block.height);
|
||||
|
@ -503,7 +503,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
|
||||
{
|
||||
checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1);
|
||||
|
||||
WaitableLock lock(g_best_block_mutex);
|
||||
WAIT_LOCK(g_best_block_mutex, lock);
|
||||
while (g_best_block == hashWatchedChain && IsRPCRunning())
|
||||
{
|
||||
if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout)
|
||||
|
@ -10,9 +10,9 @@
|
||||
#include <fs.h>
|
||||
#include <logging.h>
|
||||
#include <streams.h>
|
||||
#include <threadsafety.h>
|
||||
#include <utilstrencodings.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
@ -169,8 +169,8 @@ static __attribute__((noinline)) std::vector<uint64_t> GetStackFrames(size_t ski
|
||||
static BOOL symInitialized = SymInitialize(GetCurrentProcess(), nullptr, TRUE);
|
||||
|
||||
// dbghelp is not thread safe
|
||||
static std::mutex m;
|
||||
std::lock_guard<std::mutex> l(m);
|
||||
static StdMutex m;
|
||||
StdLockGuard l(m);
|
||||
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE thread = GetCurrentThread();
|
||||
@ -531,7 +531,7 @@ static void PrintCrashInfo(const crash_info& ci)
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CRASH_HOOKS
|
||||
static std::mutex g_stacktraces_mutex;
|
||||
static StdMutex g_stacktraces_mutex;
|
||||
static std::map<void*, std::shared_ptr<std::vector<uint64_t>>> g_stacktraces;
|
||||
|
||||
#if CRASH_HOOKS_WRAPPED_CXX_ABI
|
||||
@ -594,7 +594,7 @@ extern "C" void* __attribute__((noinline)) WRAPPED_NAME(__cxa_allocate_exception
|
||||
|
||||
void* p = __real___cxa_allocate_exception(thrown_size);
|
||||
|
||||
std::lock_guard<std::mutex> l(g_stacktraces_mutex);
|
||||
StdLockGuard l(g_stacktraces_mutex);
|
||||
g_stacktraces.emplace(p, st);
|
||||
return p;
|
||||
}
|
||||
@ -603,7 +603,7 @@ extern "C" void __attribute__((noinline)) WRAPPED_NAME(__cxa_free_exception)(voi
|
||||
{
|
||||
__real___cxa_free_exception(thrown_exception);
|
||||
|
||||
std::lock_guard<std::mutex> l(g_stacktraces_mutex);
|
||||
StdLockGuard l(g_stacktraces_mutex);
|
||||
g_stacktraces.erase(thrown_exception);
|
||||
}
|
||||
|
||||
@ -667,7 +667,7 @@ static std::shared_ptr<std::vector<uint64_t>> GetExceptionStacktrace(const std::
|
||||
#ifdef ENABLE_CRASH_HOOKS
|
||||
void* p = *(void**)&e;
|
||||
|
||||
std::lock_guard<std::mutex> l(g_stacktraces_mutex);
|
||||
StdLockGuard l(g_stacktraces_mutex);
|
||||
auto it = g_stacktraces.find(p);
|
||||
if (it == g_stacktraces.end()) {
|
||||
return nullptr;
|
||||
|
13
src/sync.cpp
13
src/sync.cpp
@ -104,7 +104,11 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
|
||||
printf("%s\n", strOutput.c_str());
|
||||
LogPrintf("%s\n", strOutput.c_str());
|
||||
|
||||
assert(false);
|
||||
if (g_debug_lockorder_abort) {
|
||||
fprintf(stderr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__);
|
||||
abort();
|
||||
}
|
||||
throw std::logic_error("potential deadlock detected");
|
||||
}
|
||||
|
||||
static void push_lock(void* c, const CLockLocation& locklocation)
|
||||
@ -152,7 +156,8 @@ std::string LocksHeld()
|
||||
return result;
|
||||
}
|
||||
|
||||
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
|
||||
template <typename MutexType>
|
||||
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs)
|
||||
{
|
||||
for (const std::pair<void*, CLockLocation>& i : g_lockstack)
|
||||
if (i.first == cs)
|
||||
@ -160,6 +165,8 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine,
|
||||
fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
|
||||
abort();
|
||||
}
|
||||
template void AssertLockHeldInternal(const char*, const char*, int, Mutex*);
|
||||
template void AssertLockHeldInternal(const char*, const char*, int, CCriticalSection*);
|
||||
|
||||
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
|
||||
{
|
||||
@ -193,4 +200,6 @@ void DeleteLock(void* cs)
|
||||
}
|
||||
}
|
||||
|
||||
bool g_debug_lockorder_abort = true;
|
||||
|
||||
#endif /* DEBUG_LOCKORDER */
|
||||
|
120
src/sync.h
120
src/sync.h
@ -46,14 +46,44 @@ LEAVE_CRITICAL_SECTION(mutex); // no RAII
|
||||
// //
|
||||
///////////////////////////////
|
||||
|
||||
#ifdef DEBUG_LOCKORDER
|
||||
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
|
||||
void LeaveCritical();
|
||||
std::string LocksHeld();
|
||||
template <typename MutexType>
|
||||
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs);
|
||||
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
|
||||
void DeleteLock(void* cs);
|
||||
|
||||
/**
|
||||
* Template mixin that adds -Wthread-safety locking
|
||||
* annotations to a subset of the mutex API.
|
||||
* 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
|
||||
void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
|
||||
void static inline LeaveCritical() {}
|
||||
template <typename MutexType>
|
||||
void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
|
||||
void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
|
||||
void static inline DeleteLock(void* cs) {}
|
||||
#endif
|
||||
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
|
||||
#define AssertLockNotHeld(cs) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs)
|
||||
|
||||
/**
|
||||
* Template mixin that adds -Wthread-safety locking annotations and lock order
|
||||
* checking to a subset of the mutex API.
|
||||
*/
|
||||
template <typename PARENT>
|
||||
class LOCKABLE AnnotatedMixin : public PARENT
|
||||
{
|
||||
public:
|
||||
~AnnotatedMixin() {
|
||||
DeleteLock((void*)this);
|
||||
}
|
||||
|
||||
void lock() EXCLUSIVE_LOCK_FUNCTION()
|
||||
{
|
||||
PARENT::lock();
|
||||
@ -68,64 +98,42 @@ public:
|
||||
{
|
||||
return PARENT::try_lock();
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef DEBUG_LOCKORDER
|
||||
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
|
||||
void LeaveCritical();
|
||||
std::string LocksHeld();
|
||||
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs);
|
||||
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
|
||||
void DeleteLock(void* cs);
|
||||
#else
|
||||
void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
|
||||
void static inline LeaveCritical() {}
|
||||
void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs) {}
|
||||
void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
|
||||
void static inline DeleteLock(void* cs) {}
|
||||
#endif
|
||||
#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
|
||||
#define AssertLockNotHeld(cs) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs)
|
||||
using UniqueLock = std::unique_lock<PARENT>;
|
||||
#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__
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapped mutex: supports recursive locking, but no waiting
|
||||
* TODO: We should move away from using the recursive lock by default.
|
||||
*/
|
||||
class CCriticalSection : public AnnotatedMixin<std::recursive_mutex>
|
||||
{
|
||||
public:
|
||||
~CCriticalSection() {
|
||||
DeleteLock((void*)this);
|
||||
}
|
||||
};
|
||||
typedef AnnotatedMixin<std::recursive_mutex> CCriticalSection;
|
||||
|
||||
/** Wrapped mutex: supports waiting but not recursive locking */
|
||||
typedef AnnotatedMixin<std::mutex> CWaitableCriticalSection;
|
||||
|
||||
/** Just a typedef for std::condition_variable, can be wrapped later if desired */
|
||||
typedef std::condition_variable CConditionVariable;
|
||||
|
||||
/** Just a typedef for std::unique_lock, can be wrapped later if desired */
|
||||
typedef std::unique_lock<std::mutex> WaitableLock;
|
||||
typedef AnnotatedMixin<std::mutex> Mutex;
|
||||
|
||||
#ifdef DEBUG_LOCKCONTENTION
|
||||
void PrintLockContention(const char* pszName, const char* pszFile, int nLine);
|
||||
#endif
|
||||
|
||||
/** Wrapper around std::unique_lock<CCriticalSection> */
|
||||
class SCOPED_LOCKABLE CCriticalBlock
|
||||
/** Wrapper around std::unique_lock style lock for Mutex. */
|
||||
template <typename Mutex, typename Base = typename Mutex::UniqueLock>
|
||||
class SCOPED_LOCKABLE UniqueLock : public Base
|
||||
{
|
||||
private:
|
||||
std::unique_lock<CCriticalSection> lock;
|
||||
|
||||
void Enter(const char* pszName, const char* pszFile, int nLine)
|
||||
{
|
||||
EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()));
|
||||
EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex()));
|
||||
#ifdef DEBUG_LOCKCONTENTION
|
||||
if (!lock.try_lock()) {
|
||||
if (!Base::try_lock()) {
|
||||
PrintLockContention(pszName, pszFile, nLine);
|
||||
#endif
|
||||
lock.lock();
|
||||
Base::lock();
|
||||
#ifdef DEBUG_LOCKCONTENTION
|
||||
}
|
||||
#endif
|
||||
@ -133,15 +141,15 @@ private:
|
||||
|
||||
bool TryEnter(const char* pszName, const char* pszFile, int nLine)
|
||||
{
|
||||
EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true);
|
||||
lock.try_lock();
|
||||
if (!lock.owns_lock())
|
||||
EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex()), true);
|
||||
Base::try_lock();
|
||||
if (!Base::owns_lock())
|
||||
LeaveCritical();
|
||||
return lock.owns_lock();
|
||||
return Base::owns_lock();
|
||||
}
|
||||
|
||||
public:
|
||||
CCriticalBlock(CCriticalSection& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, std::defer_lock)
|
||||
UniqueLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
|
||||
{
|
||||
if (fTry)
|
||||
TryEnter(pszName, pszFile, nLine);
|
||||
@ -149,35 +157,41 @@ public:
|
||||
Enter(pszName, pszFile, nLine);
|
||||
}
|
||||
|
||||
CCriticalBlock(CCriticalSection* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
|
||||
UniqueLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
|
||||
{
|
||||
if (!pmutexIn) return;
|
||||
|
||||
lock = std::unique_lock<CCriticalSection>(*pmutexIn, std::defer_lock);
|
||||
*static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock);
|
||||
if (fTry)
|
||||
TryEnter(pszName, pszFile, nLine);
|
||||
else
|
||||
Enter(pszName, pszFile, nLine);
|
||||
}
|
||||
|
||||
~CCriticalBlock() UNLOCK_FUNCTION()
|
||||
~UniqueLock() UNLOCK_FUNCTION()
|
||||
{
|
||||
if (lock.owns_lock())
|
||||
if (Base::owns_lock())
|
||||
LeaveCritical();
|
||||
}
|
||||
|
||||
operator bool()
|
||||
{
|
||||
return lock.owns_lock();
|
||||
return Base::owns_lock();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename MutexArg>
|
||||
using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>;
|
||||
|
||||
#define PASTE(x, y) x ## y
|
||||
#define PASTE2(x, y) PASTE(x, y)
|
||||
|
||||
#define LOCK(cs) CCriticalBlock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
|
||||
#define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__)
|
||||
#define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true)
|
||||
#define LOCK(cs) DebugLock<decltype(cs)> PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
|
||||
#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)
|
||||
#define WAIT_LOCK(cs, name) DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__)
|
||||
|
||||
#define ENTER_CRITICAL_SECTION(cs) \
|
||||
{ \
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <sync.h>
|
||||
#include <util.h>
|
||||
#include <utiltime.h>
|
||||
#include <validation.h>
|
||||
@ -59,14 +60,14 @@ struct FailingCheck {
|
||||
};
|
||||
|
||||
struct UniqueCheck {
|
||||
static std::mutex m;
|
||||
static std::unordered_multiset<size_t> results;
|
||||
static Mutex m;
|
||||
static std::unordered_multiset<size_t> results GUARDED_BY(m);
|
||||
size_t check_id;
|
||||
UniqueCheck(size_t check_id_in) : check_id(check_id_in){};
|
||||
UniqueCheck() : check_id(0){};
|
||||
bool operator()()
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m);
|
||||
LOCK(m);
|
||||
results.insert(check_id);
|
||||
return true;
|
||||
}
|
||||
@ -129,7 +130,7 @@ struct FrozenCleanupCheck {
|
||||
std::mutex FrozenCleanupCheck::m{};
|
||||
std::atomic<uint64_t> FrozenCleanupCheck::nFrozen{0};
|
||||
std::condition_variable FrozenCleanupCheck::cv{};
|
||||
std::mutex UniqueCheck::m;
|
||||
Mutex UniqueCheck::m;
|
||||
std::unordered_multiset<size_t> UniqueCheck::results;
|
||||
std::atomic<size_t> FakeCheckCheckCompletion::n_calls{0};
|
||||
std::atomic<size_t> MemoryCheck::fake_allocated_memory{0};
|
||||
@ -293,11 +294,15 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
|
||||
control.Add(vChecks);
|
||||
}
|
||||
}
|
||||
bool r = true;
|
||||
BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
|
||||
for (size_t i = 0; i < COUNT; ++i)
|
||||
r = r && UniqueCheck::results.count(i) == 1;
|
||||
BOOST_REQUIRE(r);
|
||||
{
|
||||
LOCK(UniqueCheck::m);
|
||||
bool r = true;
|
||||
BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
|
||||
for (size_t i = 0; i < COUNT; ++i) {
|
||||
r = r && UniqueCheck::results.count(i) == 1;
|
||||
}
|
||||
BOOST_REQUIRE(r);
|
||||
}
|
||||
tg.interrupt_all();
|
||||
tg.join_all();
|
||||
}
|
||||
@ -442,4 +447,3 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks)
|
||||
}
|
||||
}
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
|
52
src/test/sync_tests.cpp
Normal file
52
src/test/sync_tests.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2012-2017 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <sync.h>
|
||||
#include <test/test_dash.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
namespace {
|
||||
template <typename MutexType>
|
||||
void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2)
|
||||
{
|
||||
{
|
||||
LOCK2(mutex1, mutex2);
|
||||
}
|
||||
bool error_thrown = false;
|
||||
try {
|
||||
LOCK2(mutex2, mutex1);
|
||||
} catch (const std::logic_error& e) {
|
||||
BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected");
|
||||
error_thrown = true;
|
||||
}
|
||||
#ifdef DEBUG_LOCKORDER
|
||||
BOOST_CHECK(error_thrown);
|
||||
#else
|
||||
BOOST_CHECK(!error_thrown);
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(sync_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(potential_deadlock_detected)
|
||||
{
|
||||
#ifdef DEBUG_LOCKORDER
|
||||
bool prev = g_debug_lockorder_abort;
|
||||
g_debug_lockorder_abort = false;
|
||||
#endif
|
||||
|
||||
CCriticalSection rmutex1, rmutex2;
|
||||
TestPotentialDeadLockDetected(rmutex1, rmutex2);
|
||||
|
||||
Mutex mutex1, mutex2;
|
||||
TestPotentialDeadLockDetected(mutex1, mutex2);
|
||||
|
||||
#ifdef DEBUG_LOCKORDER
|
||||
g_debug_lockorder_abort = prev;
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
@ -4,6 +4,7 @@
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <threadinterrupt.h>
|
||||
#include <sync.h>
|
||||
|
||||
CThreadInterrupt::CThreadInterrupt() : flag(false) {}
|
||||
|
||||
@ -20,7 +21,7 @@ void CThreadInterrupt::reset()
|
||||
void CThreadInterrupt::operator()()
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mut);
|
||||
LOCK(mut);
|
||||
flag.store(true, std::memory_order_release);
|
||||
}
|
||||
cond.notify_all();
|
||||
@ -28,7 +29,7 @@ void CThreadInterrupt::operator()()
|
||||
|
||||
bool CThreadInterrupt::sleep_for(std::chrono::milliseconds rel_time)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mut);
|
||||
WAIT_LOCK(mut, lock);
|
||||
return !cond.wait_for(lock, rel_time, [this]() { return flag.load(std::memory_order_acquire); });
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
#ifndef BITCOIN_THREADINTERRUPT_H
|
||||
#define BITCOIN_THREADINTERRUPT_H
|
||||
|
||||
#include <sync.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
@ -28,7 +30,7 @@ public:
|
||||
|
||||
private:
|
||||
std::condition_variable cond;
|
||||
std::mutex mut;
|
||||
Mutex mut;
|
||||
std::atomic<bool> flag;
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
#ifndef BITCOIN_THREADSAFETY_H
|
||||
#define BITCOIN_THREADSAFETY_H
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#ifdef __clang__
|
||||
// TL;DR Add GUARDED_BY(mutex) to member variables. The others are
|
||||
// rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo);
|
||||
@ -54,4 +56,26 @@
|
||||
#define ASSERT_EXCLUSIVE_LOCK(...)
|
||||
#endif // __GNUC__
|
||||
|
||||
// StdMutex provides an annotated version of std::mutex for us,
|
||||
// and should only be used when sync.h Mutex/LOCK/etc are not usable.
|
||||
class LOCKABLE StdMutex : public std::mutex
|
||||
{
|
||||
public:
|
||||
#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 StdMutex& operator!() const { return *this; }
|
||||
#endif // __clang__
|
||||
};
|
||||
|
||||
// StdLockGuard provides an annotated version of std::lock_guard for us,
|
||||
// and should only be used when sync.h Mutex/LOCK/etc are not usable.
|
||||
class SCOPED_LOCKABLE StdLockGuard : public std::lock_guard<StdMutex>
|
||||
{
|
||||
public:
|
||||
explicit StdLockGuard(StdMutex& cs) EXCLUSIVE_LOCK_FUNCTION(cs) : std::lock_guard<StdMutex>(cs) {}
|
||||
~StdLockGuard() UNLOCK_FUNCTION() {}
|
||||
};
|
||||
|
||||
#endif // BITCOIN_THREADSAFETY_H
|
||||
|
11
src/util.cpp
11
src/util.cpp
@ -153,18 +153,19 @@ public:
|
||||
}
|
||||
instance_of_cinit;
|
||||
|
||||
/** Mutex to protect dir_locks. */
|
||||
static Mutex cs_dir_locks;
|
||||
|
||||
/** A map that contains all the currently held directory locks. After
|
||||
* successful locking, these will be held here until the global destructor
|
||||
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
|
||||
* is called.
|
||||
*/
|
||||
static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks;
|
||||
/** Mutex to protect dir_locks. */
|
||||
static std::mutex cs_dir_locks;
|
||||
static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks GUARDED_BY(cs_dir_locks);
|
||||
|
||||
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only)
|
||||
{
|
||||
std::lock_guard<std::mutex> ulock(cs_dir_locks);
|
||||
LOCK(cs_dir_locks);
|
||||
fs::path pathLockFile = directory / lockfile_name;
|
||||
|
||||
// If a lock for this directory already exists in the map, don't try to re-lock it
|
||||
@ -188,7 +189,7 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b
|
||||
|
||||
void ReleaseDirectoryLocks()
|
||||
{
|
||||
std::lock_guard<std::mutex> ulock(cs_dir_locks);
|
||||
LOCK(cs_dir_locks);
|
||||
dir_locks.clear();
|
||||
}
|
||||
|
||||
|
@ -229,8 +229,8 @@ BlockMap& mapBlockIndex = g_chainstate.mapBlockIndex;
|
||||
PrevBlockMap& mapPrevBlockIndex = g_chainstate.mapPrevBlockIndex;
|
||||
CChain& chainActive = g_chainstate.chainActive;
|
||||
CBlockIndex *pindexBestHeader = nullptr;
|
||||
CWaitableCriticalSection g_best_block_mutex;
|
||||
CConditionVariable g_best_block_cv;
|
||||
Mutex g_best_block_mutex;
|
||||
std::condition_variable g_best_block_cv;
|
||||
uint256 g_best_block;
|
||||
int nScriptCheckThreads = 0;
|
||||
std::atomic_bool fImporting(false);
|
||||
@ -2605,7 +2605,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
|
||||
mempool.AddTransactionsUpdated(1);
|
||||
|
||||
{
|
||||
WaitableLock lock(g_best_block_mutex);
|
||||
LOCK(g_best_block_mutex);
|
||||
g_best_block = pindexNew->GetBlockHash();
|
||||
g_best_block_cv.notify_all();
|
||||
}
|
||||
|
@ -157,8 +157,8 @@ extern PrevBlockMap& mapPrevBlockIndex;
|
||||
extern uint64_t nLastBlockTx;
|
||||
extern uint64_t nLastBlockSize;
|
||||
extern const std::string strMessageMagic;
|
||||
extern CWaitableCriticalSection g_best_block_mutex;
|
||||
extern CConditionVariable g_best_block_cv;
|
||||
extern Mutex g_best_block_mutex;
|
||||
extern std::condition_variable g_best_block_cv;
|
||||
extern uint256 g_best_block;
|
||||
extern std::atomic_bool fImporting;
|
||||
extern std::atomic_bool fReindex;
|
||||
|
@ -703,7 +703,6 @@ private:
|
||||
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
|
||||
std::atomic<int64_t> m_scanning_start{0};
|
||||
std::atomic<double> m_scanning_progress{0};
|
||||
std::mutex mutexScanning;
|
||||
friend class WalletRescanReserver;
|
||||
|
||||
WalletBatch *encrypted_batch = nullptr;
|
||||
@ -1332,13 +1331,11 @@ public:
|
||||
bool reserve()
|
||||
{
|
||||
assert(!m_could_reserve);
|
||||
std::lock_guard<std::mutex> lock(m_wallet->mutexScanning);
|
||||
if (m_wallet->fScanningWallet) {
|
||||
if (m_wallet->fScanningWallet.exchange(true)) {
|
||||
return false;
|
||||
}
|
||||
m_wallet->m_scanning_start = GetTimeMillis();
|
||||
m_wallet->m_scanning_progress = 0;
|
||||
m_wallet->fScanningWallet = true;
|
||||
m_could_reserve = true;
|
||||
return true;
|
||||
}
|
||||
@ -1350,7 +1347,6 @@ public:
|
||||
|
||||
~WalletRescanReserver()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_wallet->mutexScanning);
|
||||
if (m_could_reserve) {
|
||||
m_wallet->fScanningWallet = false;
|
||||
}
|
||||
|
@ -14,8 +14,17 @@ class ConfArgsTest(BitcoinTestFramework):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
|
||||
|
||||
def test_log_buffer(self):
|
||||
with self.nodes[0].assert_debug_log(expected_msgs=['Warning: parsed potentially confusing double-negative -connect=0']):
|
||||
self.start_node(0, extra_args=['-noconnect=0'])
|
||||
self.stop_node(0)
|
||||
|
||||
def run_test(self):
|
||||
self.stop_node(0)
|
||||
|
||||
self.test_log_buffer()
|
||||
|
||||
# Remove the -datadir argument so it doesn't override the config file
|
||||
self.nodes[0].args = [arg for arg in self.nodes[0].args if not arg.startswith("-datadir")]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user