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")
|
AC_MSG_ERROR("enable-werror set but -Werror is not usable")
|
||||||
fi
|
fi
|
||||||
AX_CHECK_COMPILE_FLAG([-Werror=vla],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=vla"],,[[$CXXFLAG_WERROR]])
|
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
|
fi
|
||||||
|
|
||||||
if test "x$CXXFLAGS_overridden" = "xno"; then
|
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([-Wformat],[CXXFLAGS="$CXXFLAGS -Wformat"],,[[$CXXFLAG_WERROR]])
|
||||||
AX_CHECK_COMPILE_FLAG([-Wvla],[CXXFLAGS="$CXXFLAGS -Wvla"],,[[$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([-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]])
|
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
|
## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all
|
||||||
|
@ -101,6 +101,7 @@ BITCOIN_TESTS =\
|
|||||||
test/skiplist_tests.cpp \
|
test/skiplist_tests.cpp \
|
||||||
test/streams_tests.cpp \
|
test/streams_tests.cpp \
|
||||||
test/subsidy_tests.cpp \
|
test/subsidy_tests.cpp \
|
||||||
|
test/sync_tests.cpp \
|
||||||
test/timedata_tests.cpp \
|
test/timedata_tests.cpp \
|
||||||
test/torcontrol_tests.cpp \
|
test/torcontrol_tests.cpp \
|
||||||
test/transaction_tests.cpp \
|
test/transaction_tests.cpp \
|
||||||
|
@ -106,13 +106,12 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c
|
|||||||
std::vector<bool> have_txn(txn_available.size());
|
std::vector<bool> have_txn(txn_available.size());
|
||||||
{
|
{
|
||||||
LOCK(pool->cs);
|
LOCK(pool->cs);
|
||||||
const std::vector<std::pair<uint256, CTxMemPool::txiter> >& vTxHashes = pool->vTxHashes;
|
for (size_t i = 0; i < pool->vTxHashes.size(); i++) {
|
||||||
for (size_t i = 0; i < vTxHashes.size(); i++) {
|
uint64_t shortid = cmpctblock.GetShortID(pool->vTxHashes[i].first);
|
||||||
uint64_t shortid = cmpctblock.GetShortID(vTxHashes[i].first);
|
|
||||||
std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);
|
std::unordered_map<uint64_t, uint16_t>::iterator idit = shorttxids.find(shortid);
|
||||||
if (idit != shorttxids.end()) {
|
if (idit != shorttxids.end()) {
|
||||||
if (!have_txn[idit->second]) {
|
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;
|
have_txn[idit->second] = true;
|
||||||
mempool_count++;
|
mempool_count++;
|
||||||
} else {
|
} else {
|
||||||
|
@ -127,7 +127,7 @@ class PartiallyDownloadedBlock {
|
|||||||
protected:
|
protected:
|
||||||
std::vector<CTransactionRef> txn_available;
|
std::vector<CTransactionRef> txn_available;
|
||||||
size_t prefilled_count = 0, mempool_count = 0, extra_count = 0;
|
size_t prefilled_count = 0, mempool_count = 0, extra_count = 0;
|
||||||
CTxMemPool* pool;
|
const CTxMemPool* pool;
|
||||||
public:
|
public:
|
||||||
CBlockHeader header;
|
CBlockHeader header;
|
||||||
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
|
explicit PartiallyDownloadedBlock(CTxMemPool* poolIn) : pool(poolIn) {}
|
||||||
|
@ -74,7 +74,7 @@ class WorkQueue
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
/** Mutex protects entire object */
|
/** Mutex protects entire object */
|
||||||
std::mutex cs;
|
Mutex cs;
|
||||||
std::condition_variable cond;
|
std::condition_variable cond;
|
||||||
std::deque<std::unique_ptr<WorkItem>> queue;
|
std::deque<std::unique_ptr<WorkItem>> queue;
|
||||||
bool running;
|
bool running;
|
||||||
@ -93,7 +93,7 @@ public:
|
|||||||
/** Enqueue a work item */
|
/** Enqueue a work item */
|
||||||
bool Enqueue(WorkItem* item)
|
bool Enqueue(WorkItem* item)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(cs);
|
LOCK(cs);
|
||||||
if (queue.size() >= maxDepth) {
|
if (queue.size() >= maxDepth) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -107,7 +107,7 @@ public:
|
|||||||
while (true) {
|
while (true) {
|
||||||
std::unique_ptr<WorkItem> i;
|
std::unique_ptr<WorkItem> i;
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(cs);
|
WAIT_LOCK(cs, lock);
|
||||||
while (running && queue.empty())
|
while (running && queue.empty())
|
||||||
cond.wait(lock);
|
cond.wait(lock);
|
||||||
if (!running)
|
if (!running)
|
||||||
@ -121,7 +121,7 @@ public:
|
|||||||
/** Interrupt and exit loops */
|
/** Interrupt and exit loops */
|
||||||
void Interrupt()
|
void Interrupt()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(cs);
|
LOCK(cs);
|
||||||
running = false;
|
running = false;
|
||||||
cond.notify_all();
|
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 bool fHaveGenesis = false;
|
||||||
static CWaitableCriticalSection cs_GenesisWait;
|
static Mutex g_genesis_wait_mutex;
|
||||||
static CConditionVariable condvar_GenesisWait;
|
static std::condition_variable g_genesis_wait_cv;
|
||||||
|
|
||||||
static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex)
|
static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex)
|
||||||
{
|
{
|
||||||
if (pBlockIndex != nullptr) {
|
if (pBlockIndex != nullptr) {
|
||||||
{
|
{
|
||||||
WaitableLock lock_GenesisWait(cs_GenesisWait);
|
LOCK(g_genesis_wait_mutex);
|
||||||
fHaveGenesis = true;
|
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_print_to_file = !gArgs.IsArgNegated("-debuglogfile");
|
||||||
g_logger->m_file_path = AbsPathForConfigVal(gArgs.GetArg("-debuglogfile", DEFAULT_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_print_to_console = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false));
|
||||||
g_logger->m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
|
g_logger->m_log_timestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS);
|
||||||
g_logger->m_log_time_micros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS);
|
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
|
// and because this needs to happen before any other debug.log printing
|
||||||
g_logger->ShrinkDebugFile();
|
g_logger->ShrinkDebugFile();
|
||||||
}
|
}
|
||||||
if (!g_logger->OpenDebugLog()) {
|
}
|
||||||
return InitError(strprintf("Could not open debug log file %s",
|
if (!g_logger->StartLogging()) {
|
||||||
g_logger->m_file_path.string()));
|
return InitError(strprintf("Could not open debug log file %s",
|
||||||
}
|
g_logger->m_file_path.string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_logger->m_log_timestamps)
|
if (!g_logger->m_log_timestamps)
|
||||||
@ -2377,12 +2371,12 @@ bool AppInitMain()
|
|||||||
|
|
||||||
// Wait for genesis block to be processed
|
// 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
|
// We previously could hang here if StartShutdown() is called prior to
|
||||||
// ThreadImport getting started, so instead we just wait on a timer to
|
// ThreadImport getting started, so instead we just wait on a timer to
|
||||||
// check ShutdownRequested() regularly.
|
// check ShutdownRequested() regularly.
|
||||||
while (!fHaveGenesis && !ShutdownRequested()) {
|
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);
|
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);
|
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_fileout == nullptr);
|
||||||
assert(!m_file_path.empty());
|
|
||||||
|
|
||||||
m_fileout = fsbridge::fopen(m_file_path, "a");
|
if (m_print_to_file) {
|
||||||
if (!m_fileout) {
|
assert(!m_file_path.empty());
|
||||||
return false;
|
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
|
// dump buffered messages from before we opened the log
|
||||||
|
m_buffering = false;
|
||||||
while (!m_msgs_before_open.empty()) {
|
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();
|
m_msgs_before_open.pop_front();
|
||||||
}
|
}
|
||||||
|
if (m_print_to_console) fflush(stdout);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -247,8 +261,9 @@ std::string BCLog::Logger::LogThreadNameStr(const std::string &str)
|
|||||||
return strThreadLogged;
|
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 strThreadLogged = LogThreadNameStr(str);
|
||||||
std::string strTimestamped = LogTimestampStr(strThreadLogged);
|
std::string strTimestamped = LogTimestampStr(strThreadLogged);
|
||||||
|
|
||||||
@ -257,32 +272,31 @@ void BCLog::Logger::LogPrintStr(const std::string &str)
|
|||||||
else
|
else
|
||||||
m_started_new_line = false;
|
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) {
|
if (m_print_to_console) {
|
||||||
// print to console
|
// print to console
|
||||||
fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
|
fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
if (m_print_to_file) {
|
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
|
// reopen the log file, if requested
|
||||||
if (m_fileout == nullptr) {
|
if (m_reopen_file) {
|
||||||
m_msgs_before_open.push_back(strTimestamped);
|
m_reopen_file = false;
|
||||||
}
|
FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
|
||||||
else
|
if (new_fileout) {
|
||||||
{
|
setbuf(new_fileout, nullptr); // unbuffered
|
||||||
// reopen the log file, if requested
|
fclose(m_fileout);
|
||||||
if (m_reopen_file) {
|
m_fileout = new_fileout;
|
||||||
m_reopen_file = false;
|
|
||||||
m_fileout = fsbridge::freopen(m_file_path, "a", m_fileout);
|
|
||||||
if (!m_fileout) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setbuf(m_fileout, nullptr); // unbuffered
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FileWriteStr(strTimestamped, m_fileout);
|
|
||||||
}
|
}
|
||||||
|
FileWriteStr(strTimestamped, m_fileout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <fs.h>
|
#include <fs.h>
|
||||||
#include <tinyformat.h>
|
#include <tinyformat.h>
|
||||||
|
#include <threadsafety.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@ -82,9 +83,11 @@ namespace BCLog {
|
|||||||
class Logger
|
class Logger
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
FILE* m_fileout = nullptr;
|
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
|
||||||
std::mutex m_file_mutex;
|
|
||||||
std::list<std::string> m_msgs_before_open;
|
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
|
* 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};
|
std::atomic<bool> m_reopen_file{false};
|
||||||
|
|
||||||
/** Send a string to the log output */
|
/** 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 */
|
/** 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();
|
void ShrinkDebugFile();
|
||||||
|
|
||||||
uint64_t GetCategoryMask() const { return m_categories.load(); }
|
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()
|
void CConnman::WakeMessageHandler()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutexMsgProc);
|
LOCK(mutexMsgProc);
|
||||||
fMsgProcWake = true;
|
fMsgProcWake = true;
|
||||||
}
|
}
|
||||||
condMsgProc.notify_one();
|
condMsgProc.notify_one();
|
||||||
@ -2862,9 +2862,9 @@ void CConnman::ThreadMessageHandler()
|
|||||||
|
|
||||||
ReleaseNodeVector(vNodesCopy);
|
ReleaseNodeVector(vNodesCopy);
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(mutexMsgProc);
|
WAIT_LOCK(mutexMsgProc, lock);
|
||||||
if (!fMoreWork) {
|
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;
|
fMsgProcWake = false;
|
||||||
}
|
}
|
||||||
@ -3198,7 +3198,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
|
|||||||
flagInterruptMsgProc = false;
|
flagInterruptMsgProc = false;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(mutexMsgProc);
|
LOCK(mutexMsgProc);
|
||||||
fMsgProcWake = false;
|
fMsgProcWake = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3303,7 +3303,7 @@ void CExplicitNetCleanup::callCleanup()
|
|||||||
void CConnman::Interrupt()
|
void CConnman::Interrupt()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(mutexMsgProc);
|
LOCK(mutexMsgProc);
|
||||||
flagInterruptMsgProc = true;
|
flagInterruptMsgProc = true;
|
||||||
}
|
}
|
||||||
condMsgProc.notify_all();
|
condMsgProc.notify_all();
|
||||||
|
@ -617,10 +617,10 @@ private:
|
|||||||
const uint64_t nSeed0, nSeed1;
|
const uint64_t nSeed0, nSeed1;
|
||||||
|
|
||||||
/** flag for waking the message processor. */
|
/** flag for waking the message processor. */
|
||||||
bool fMsgProcWake;
|
bool fMsgProcWake GUARDED_BY(mutexMsgProc);
|
||||||
|
|
||||||
std::condition_variable condMsgProc;
|
std::condition_variable condMsgProc;
|
||||||
std::mutex mutexMsgProc;
|
Mutex mutexMsgProc;
|
||||||
std::atomic<bool> flagInterruptMsgProc;
|
std::atomic<bool> flagInterruptMsgProc;
|
||||||
|
|
||||||
CThreadInterrupt interruptNet;
|
CThreadInterrupt interruptNet;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <wincrypt.h>
|
#include <wincrypt.h>
|
||||||
#endif
|
#endif
|
||||||
#include <logging.h> // for LogPrint()
|
#include <logging.h> // for LogPrint()
|
||||||
|
#include <sync.h> // for WAIT_LOCK
|
||||||
#include <utiltime.h> // for GetTime()
|
#include <utiltime.h> // for GetTime()
|
||||||
|
|
||||||
#include <stdlib.h>
|
#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 unsigned char rng_state[32] = {0};
|
||||||
static uint64_t rng_counter = 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);
|
hasher.Write((const unsigned char*)data, len);
|
||||||
unsigned char buf[64];
|
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(rng_state, sizeof(rng_state));
|
||||||
hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
|
hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
|
||||||
++rng_counter;
|
++rng_counter;
|
||||||
@ -337,7 +338,7 @@ void GetStrongRandBytes(unsigned char* out, int num)
|
|||||||
|
|
||||||
// Combine with and update state
|
// 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(rng_state, sizeof(rng_state));
|
||||||
hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
|
hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter));
|
||||||
++rng_counter;
|
++rng_counter;
|
||||||
|
@ -53,9 +53,9 @@ struct CUpdatedBlock
|
|||||||
int height;
|
int height;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::mutex cs_blockchange;
|
static Mutex cs_blockchange;
|
||||||
static std::condition_variable cond_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);
|
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)
|
void RPCNotifyBlockChange(bool ibd, const CBlockIndex * pindex)
|
||||||
{
|
{
|
||||||
if(pindex) {
|
if(pindex) {
|
||||||
std::lock_guard<std::mutex> lock(cs_blockchange);
|
LOCK(cs_blockchange);
|
||||||
latestblock.hash = pindex->GetBlockHash();
|
latestblock.hash = pindex->GetBlockHash();
|
||||||
latestblock.height = pindex->nHeight;
|
latestblock.height = pindex->nHeight;
|
||||||
}
|
}
|
||||||
@ -278,12 +278,12 @@ static UniValue waitfornewblock(const JSONRPCRequest& request)
|
|||||||
|
|
||||||
CUpdatedBlock block;
|
CUpdatedBlock block;
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(cs_blockchange);
|
WAIT_LOCK(cs_blockchange, lock);
|
||||||
block = latestblock;
|
block = latestblock;
|
||||||
if(timeout)
|
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
|
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;
|
block = latestblock;
|
||||||
}
|
}
|
||||||
UniValue ret(UniValue::VOBJ);
|
UniValue ret(UniValue::VOBJ);
|
||||||
@ -320,11 +320,11 @@ static UniValue waitforblock(const JSONRPCRequest& request)
|
|||||||
|
|
||||||
CUpdatedBlock block;
|
CUpdatedBlock block;
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(cs_blockchange);
|
WAIT_LOCK(cs_blockchange, lock);
|
||||||
if(timeout)
|
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
|
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;
|
block = latestblock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,13 +363,14 @@ static UniValue waitforblockheight(const JSONRPCRequest& request)
|
|||||||
|
|
||||||
CUpdatedBlock block;
|
CUpdatedBlock block;
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(cs_blockchange);
|
WAIT_LOCK(cs_blockchange, lock);
|
||||||
if(timeout)
|
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
|
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;
|
block = latestblock;
|
||||||
}
|
}
|
||||||
|
// TODO: Backport g_utxosetscan and associated logic from #16127
|
||||||
UniValue ret(UniValue::VOBJ);
|
UniValue ret(UniValue::VOBJ);
|
||||||
ret.pushKV("hash", block.hash.GetHex());
|
ret.pushKV("hash", block.hash.GetHex());
|
||||||
ret.pushKV("height", block.height);
|
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);
|
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())
|
while (g_best_block == hashWatchedChain && IsRPCRunning())
|
||||||
{
|
{
|
||||||
if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout)
|
if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout)
|
||||||
|
@ -10,9 +10,9 @@
|
|||||||
#include <fs.h>
|
#include <fs.h>
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
#include <streams.h>
|
#include <streams.h>
|
||||||
|
#include <threadsafety.h>
|
||||||
#include <utilstrencodings.h>
|
#include <utilstrencodings.h>
|
||||||
|
|
||||||
#include <mutex>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -169,8 +169,8 @@ static __attribute__((noinline)) std::vector<uint64_t> GetStackFrames(size_t ski
|
|||||||
static BOOL symInitialized = SymInitialize(GetCurrentProcess(), nullptr, TRUE);
|
static BOOL symInitialized = SymInitialize(GetCurrentProcess(), nullptr, TRUE);
|
||||||
|
|
||||||
// dbghelp is not thread safe
|
// dbghelp is not thread safe
|
||||||
static std::mutex m;
|
static StdMutex m;
|
||||||
std::lock_guard<std::mutex> l(m);
|
StdLockGuard l(m);
|
||||||
|
|
||||||
HANDLE process = GetCurrentProcess();
|
HANDLE process = GetCurrentProcess();
|
||||||
HANDLE thread = GetCurrentThread();
|
HANDLE thread = GetCurrentThread();
|
||||||
@ -531,7 +531,7 @@ static void PrintCrashInfo(const crash_info& ci)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_CRASH_HOOKS
|
#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;
|
static std::map<void*, std::shared_ptr<std::vector<uint64_t>>> g_stacktraces;
|
||||||
|
|
||||||
#if CRASH_HOOKS_WRAPPED_CXX_ABI
|
#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);
|
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);
|
g_stacktraces.emplace(p, st);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
@ -603,7 +603,7 @@ extern "C" void __attribute__((noinline)) WRAPPED_NAME(__cxa_free_exception)(voi
|
|||||||
{
|
{
|
||||||
__real___cxa_free_exception(thrown_exception);
|
__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);
|
g_stacktraces.erase(thrown_exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,7 +667,7 @@ static std::shared_ptr<std::vector<uint64_t>> GetExceptionStacktrace(const std::
|
|||||||
#ifdef ENABLE_CRASH_HOOKS
|
#ifdef ENABLE_CRASH_HOOKS
|
||||||
void* p = *(void**)&e;
|
void* p = *(void**)&e;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> l(g_stacktraces_mutex);
|
StdLockGuard l(g_stacktraces_mutex);
|
||||||
auto it = g_stacktraces.find(p);
|
auto it = g_stacktraces.find(p);
|
||||||
if (it == g_stacktraces.end()) {
|
if (it == g_stacktraces.end()) {
|
||||||
return nullptr;
|
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());
|
printf("%s\n", strOutput.c_str());
|
||||||
LogPrintf("%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)
|
static void push_lock(void* c, const CLockLocation& locklocation)
|
||||||
@ -152,7 +156,8 @@ std::string LocksHeld()
|
|||||||
return result;
|
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)
|
for (const std::pair<void*, CLockLocation>& i : g_lockstack)
|
||||||
if (i.first == cs)
|
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());
|
fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
|
||||||
abort();
|
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)
|
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 */
|
#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
|
* Call abort() if a potential lock order deadlock bug is detected, instead of
|
||||||
* annotations to a subset of the mutex API.
|
* 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>
|
template <typename PARENT>
|
||||||
class LOCKABLE AnnotatedMixin : public PARENT
|
class LOCKABLE AnnotatedMixin : public PARENT
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
~AnnotatedMixin() {
|
||||||
|
DeleteLock((void*)this);
|
||||||
|
}
|
||||||
|
|
||||||
void lock() EXCLUSIVE_LOCK_FUNCTION()
|
void lock() EXCLUSIVE_LOCK_FUNCTION()
|
||||||
{
|
{
|
||||||
PARENT::lock();
|
PARENT::lock();
|
||||||
@ -68,64 +98,42 @@ public:
|
|||||||
{
|
{
|
||||||
return PARENT::try_lock();
|
return PARENT::try_lock();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef DEBUG_LOCKORDER
|
using UniqueLock = std::unique_lock<PARENT>;
|
||||||
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
|
#ifdef __clang__
|
||||||
void LeaveCritical();
|
//! For negative capabilities in the Clang Thread Safety Analysis.
|
||||||
std::string LocksHeld();
|
//! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in conjunction
|
||||||
void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs);
|
//! with the ! operator, to indicate that a mutex should not be held.
|
||||||
void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
|
const AnnotatedMixin& operator!() const { return *this; }
|
||||||
void DeleteLock(void* cs);
|
#endif // __clang__
|
||||||
#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)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapped mutex: supports recursive locking, but no waiting
|
* Wrapped mutex: supports recursive locking, but no waiting
|
||||||
* TODO: We should move away from using the recursive lock by default.
|
* TODO: We should move away from using the recursive lock by default.
|
||||||
*/
|
*/
|
||||||
class CCriticalSection : public AnnotatedMixin<std::recursive_mutex>
|
typedef AnnotatedMixin<std::recursive_mutex> CCriticalSection;
|
||||||
{
|
|
||||||
public:
|
|
||||||
~CCriticalSection() {
|
|
||||||
DeleteLock((void*)this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Wrapped mutex: supports waiting but not recursive locking */
|
/** Wrapped mutex: supports waiting but not recursive locking */
|
||||||
typedef AnnotatedMixin<std::mutex> CWaitableCriticalSection;
|
typedef AnnotatedMixin<std::mutex> Mutex;
|
||||||
|
|
||||||
/** 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;
|
|
||||||
|
|
||||||
#ifdef DEBUG_LOCKCONTENTION
|
#ifdef DEBUG_LOCKCONTENTION
|
||||||
void PrintLockContention(const char* pszName, const char* pszFile, int nLine);
|
void PrintLockContention(const char* pszName, const char* pszFile, int nLine);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Wrapper around std::unique_lock<CCriticalSection> */
|
/** Wrapper around std::unique_lock style lock for Mutex. */
|
||||||
class SCOPED_LOCKABLE CCriticalBlock
|
template <typename Mutex, typename Base = typename Mutex::UniqueLock>
|
||||||
|
class SCOPED_LOCKABLE UniqueLock : public Base
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::unique_lock<CCriticalSection> lock;
|
|
||||||
|
|
||||||
void Enter(const char* pszName, const char* pszFile, int nLine)
|
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
|
#ifdef DEBUG_LOCKCONTENTION
|
||||||
if (!lock.try_lock()) {
|
if (!Base::try_lock()) {
|
||||||
PrintLockContention(pszName, pszFile, nLine);
|
PrintLockContention(pszName, pszFile, nLine);
|
||||||
#endif
|
#endif
|
||||||
lock.lock();
|
Base::lock();
|
||||||
#ifdef DEBUG_LOCKCONTENTION
|
#ifdef DEBUG_LOCKCONTENTION
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -133,15 +141,15 @@ private:
|
|||||||
|
|
||||||
bool TryEnter(const char* pszName, const char* pszFile, int nLine)
|
bool TryEnter(const char* pszName, const char* pszFile, int nLine)
|
||||||
{
|
{
|
||||||
EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true);
|
EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex()), true);
|
||||||
lock.try_lock();
|
Base::try_lock();
|
||||||
if (!lock.owns_lock())
|
if (!Base::owns_lock())
|
||||||
LeaveCritical();
|
LeaveCritical();
|
||||||
return lock.owns_lock();
|
return Base::owns_lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
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)
|
if (fTry)
|
||||||
TryEnter(pszName, pszFile, nLine);
|
TryEnter(pszName, pszFile, nLine);
|
||||||
@ -149,35 +157,41 @@ public:
|
|||||||
Enter(pszName, pszFile, nLine);
|
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;
|
if (!pmutexIn) return;
|
||||||
|
|
||||||
lock = std::unique_lock<CCriticalSection>(*pmutexIn, std::defer_lock);
|
*static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock);
|
||||||
if (fTry)
|
if (fTry)
|
||||||
TryEnter(pszName, pszFile, nLine);
|
TryEnter(pszName, pszFile, nLine);
|
||||||
else
|
else
|
||||||
Enter(pszName, pszFile, nLine);
|
Enter(pszName, pszFile, nLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
~CCriticalBlock() UNLOCK_FUNCTION()
|
~UniqueLock() UNLOCK_FUNCTION()
|
||||||
{
|
{
|
||||||
if (lock.owns_lock())
|
if (Base::owns_lock())
|
||||||
LeaveCritical();
|
LeaveCritical();
|
||||||
}
|
}
|
||||||
|
|
||||||
operator bool()
|
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 PASTE(x, y) x ## y
|
||||||
#define PASTE2(x, y) PASTE(x, y)
|
#define PASTE2(x, y) PASTE(x, y)
|
||||||
|
|
||||||
#define LOCK(cs) CCriticalBlock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
|
#define LOCK(cs) DebugLock<decltype(cs)> PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
|
||||||
#define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__)
|
#define LOCK2(cs1, cs2) \
|
||||||
#define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true)
|
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) \
|
#define ENTER_CRITICAL_SECTION(cs) \
|
||||||
{ \
|
{ \
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include <sync.h>
|
||||||
#include <util.h>
|
#include <util.h>
|
||||||
#include <utiltime.h>
|
#include <utiltime.h>
|
||||||
#include <validation.h>
|
#include <validation.h>
|
||||||
@ -59,14 +60,14 @@ struct FailingCheck {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct UniqueCheck {
|
struct UniqueCheck {
|
||||||
static std::mutex m;
|
static Mutex m;
|
||||||
static std::unordered_multiset<size_t> results;
|
static std::unordered_multiset<size_t> results GUARDED_BY(m);
|
||||||
size_t check_id;
|
size_t check_id;
|
||||||
UniqueCheck(size_t check_id_in) : check_id(check_id_in){};
|
UniqueCheck(size_t check_id_in) : check_id(check_id_in){};
|
||||||
UniqueCheck() : check_id(0){};
|
UniqueCheck() : check_id(0){};
|
||||||
bool operator()()
|
bool operator()()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(m);
|
LOCK(m);
|
||||||
results.insert(check_id);
|
results.insert(check_id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -129,7 +130,7 @@ struct FrozenCleanupCheck {
|
|||||||
std::mutex FrozenCleanupCheck::m{};
|
std::mutex FrozenCleanupCheck::m{};
|
||||||
std::atomic<uint64_t> FrozenCleanupCheck::nFrozen{0};
|
std::atomic<uint64_t> FrozenCleanupCheck::nFrozen{0};
|
||||||
std::condition_variable FrozenCleanupCheck::cv{};
|
std::condition_variable FrozenCleanupCheck::cv{};
|
||||||
std::mutex UniqueCheck::m;
|
Mutex UniqueCheck::m;
|
||||||
std::unordered_multiset<size_t> UniqueCheck::results;
|
std::unordered_multiset<size_t> UniqueCheck::results;
|
||||||
std::atomic<size_t> FakeCheckCheckCompletion::n_calls{0};
|
std::atomic<size_t> FakeCheckCheckCompletion::n_calls{0};
|
||||||
std::atomic<size_t> MemoryCheck::fake_allocated_memory{0};
|
std::atomic<size_t> MemoryCheck::fake_allocated_memory{0};
|
||||||
@ -293,11 +294,15 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
|
|||||||
control.Add(vChecks);
|
control.Add(vChecks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool r = true;
|
{
|
||||||
BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
|
LOCK(UniqueCheck::m);
|
||||||
for (size_t i = 0; i < COUNT; ++i)
|
bool r = true;
|
||||||
r = r && UniqueCheck::results.count(i) == 1;
|
BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
|
||||||
BOOST_REQUIRE(r);
|
for (size_t i = 0; i < COUNT; ++i) {
|
||||||
|
r = r && UniqueCheck::results.count(i) == 1;
|
||||||
|
}
|
||||||
|
BOOST_REQUIRE(r);
|
||||||
|
}
|
||||||
tg.interrupt_all();
|
tg.interrupt_all();
|
||||||
tg.join_all();
|
tg.join_all();
|
||||||
}
|
}
|
||||||
@ -442,4 +447,3 @@ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
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.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include <threadinterrupt.h>
|
#include <threadinterrupt.h>
|
||||||
|
#include <sync.h>
|
||||||
|
|
||||||
CThreadInterrupt::CThreadInterrupt() : flag(false) {}
|
CThreadInterrupt::CThreadInterrupt() : flag(false) {}
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ void CThreadInterrupt::reset()
|
|||||||
void CThreadInterrupt::operator()()
|
void CThreadInterrupt::operator()()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(mut);
|
LOCK(mut);
|
||||||
flag.store(true, std::memory_order_release);
|
flag.store(true, std::memory_order_release);
|
||||||
}
|
}
|
||||||
cond.notify_all();
|
cond.notify_all();
|
||||||
@ -28,7 +29,7 @@ void CThreadInterrupt::operator()()
|
|||||||
|
|
||||||
bool CThreadInterrupt::sleep_for(std::chrono::milliseconds rel_time)
|
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); });
|
return !cond.wait_for(lock, rel_time, [this]() { return flag.load(std::memory_order_acquire); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#ifndef BITCOIN_THREADINTERRUPT_H
|
#ifndef BITCOIN_THREADINTERRUPT_H
|
||||||
#define BITCOIN_THREADINTERRUPT_H
|
#define BITCOIN_THREADINTERRUPT_H
|
||||||
|
|
||||||
|
#include <sync.h>
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@ -28,7 +30,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::condition_variable cond;
|
std::condition_variable cond;
|
||||||
std::mutex mut;
|
Mutex mut;
|
||||||
std::atomic<bool> flag;
|
std::atomic<bool> flag;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#ifndef BITCOIN_THREADSAFETY_H
|
#ifndef BITCOIN_THREADSAFETY_H
|
||||||
#define BITCOIN_THREADSAFETY_H
|
#define BITCOIN_THREADSAFETY_H
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
// TL;DR Add GUARDED_BY(mutex) to member variables. The others are
|
// TL;DR Add GUARDED_BY(mutex) to member variables. The others are
|
||||||
// rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo);
|
// rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo);
|
||||||
@ -54,4 +56,26 @@
|
|||||||
#define ASSERT_EXCLUSIVE_LOCK(...)
|
#define ASSERT_EXCLUSIVE_LOCK(...)
|
||||||
#endif // __GNUC__
|
#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
|
#endif // BITCOIN_THREADSAFETY_H
|
||||||
|
11
src/util.cpp
11
src/util.cpp
@ -153,18 +153,19 @@ public:
|
|||||||
}
|
}
|
||||||
instance_of_cinit;
|
instance_of_cinit;
|
||||||
|
|
||||||
|
/** Mutex to protect dir_locks. */
|
||||||
|
static Mutex cs_dir_locks;
|
||||||
|
|
||||||
/** A map that contains all the currently held directory locks. After
|
/** A map that contains all the currently held directory locks. After
|
||||||
* successful locking, these will be held here until the global destructor
|
* successful locking, these will be held here until the global destructor
|
||||||
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
|
* cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
|
||||||
* is called.
|
* is called.
|
||||||
*/
|
*/
|
||||||
static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks;
|
static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks GUARDED_BY(cs_dir_locks);
|
||||||
/** Mutex to protect dir_locks. */
|
|
||||||
static std::mutex cs_dir_locks;
|
|
||||||
|
|
||||||
bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only)
|
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;
|
fs::path pathLockFile = directory / lockfile_name;
|
||||||
|
|
||||||
// If a lock for this directory already exists in the map, don't try to re-lock it
|
// 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()
|
void ReleaseDirectoryLocks()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> ulock(cs_dir_locks);
|
LOCK(cs_dir_locks);
|
||||||
dir_locks.clear();
|
dir_locks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,8 +229,8 @@ BlockMap& mapBlockIndex = g_chainstate.mapBlockIndex;
|
|||||||
PrevBlockMap& mapPrevBlockIndex = g_chainstate.mapPrevBlockIndex;
|
PrevBlockMap& mapPrevBlockIndex = g_chainstate.mapPrevBlockIndex;
|
||||||
CChain& chainActive = g_chainstate.chainActive;
|
CChain& chainActive = g_chainstate.chainActive;
|
||||||
CBlockIndex *pindexBestHeader = nullptr;
|
CBlockIndex *pindexBestHeader = nullptr;
|
||||||
CWaitableCriticalSection g_best_block_mutex;
|
Mutex g_best_block_mutex;
|
||||||
CConditionVariable g_best_block_cv;
|
std::condition_variable g_best_block_cv;
|
||||||
uint256 g_best_block;
|
uint256 g_best_block;
|
||||||
int nScriptCheckThreads = 0;
|
int nScriptCheckThreads = 0;
|
||||||
std::atomic_bool fImporting(false);
|
std::atomic_bool fImporting(false);
|
||||||
@ -2605,7 +2605,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar
|
|||||||
mempool.AddTransactionsUpdated(1);
|
mempool.AddTransactionsUpdated(1);
|
||||||
|
|
||||||
{
|
{
|
||||||
WaitableLock lock(g_best_block_mutex);
|
LOCK(g_best_block_mutex);
|
||||||
g_best_block = pindexNew->GetBlockHash();
|
g_best_block = pindexNew->GetBlockHash();
|
||||||
g_best_block_cv.notify_all();
|
g_best_block_cv.notify_all();
|
||||||
}
|
}
|
||||||
|
@ -157,8 +157,8 @@ extern PrevBlockMap& mapPrevBlockIndex;
|
|||||||
extern uint64_t nLastBlockTx;
|
extern uint64_t nLastBlockTx;
|
||||||
extern uint64_t nLastBlockSize;
|
extern uint64_t nLastBlockSize;
|
||||||
extern const std::string strMessageMagic;
|
extern const std::string strMessageMagic;
|
||||||
extern CWaitableCriticalSection g_best_block_mutex;
|
extern Mutex g_best_block_mutex;
|
||||||
extern CConditionVariable g_best_block_cv;
|
extern std::condition_variable g_best_block_cv;
|
||||||
extern uint256 g_best_block;
|
extern uint256 g_best_block;
|
||||||
extern std::atomic_bool fImporting;
|
extern std::atomic_bool fImporting;
|
||||||
extern std::atomic_bool fReindex;
|
extern std::atomic_bool fReindex;
|
||||||
|
@ -703,7 +703,6 @@ private:
|
|||||||
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
|
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
|
||||||
std::atomic<int64_t> m_scanning_start{0};
|
std::atomic<int64_t> m_scanning_start{0};
|
||||||
std::atomic<double> m_scanning_progress{0};
|
std::atomic<double> m_scanning_progress{0};
|
||||||
std::mutex mutexScanning;
|
|
||||||
friend class WalletRescanReserver;
|
friend class WalletRescanReserver;
|
||||||
|
|
||||||
WalletBatch *encrypted_batch = nullptr;
|
WalletBatch *encrypted_batch = nullptr;
|
||||||
@ -1332,13 +1331,11 @@ public:
|
|||||||
bool reserve()
|
bool reserve()
|
||||||
{
|
{
|
||||||
assert(!m_could_reserve);
|
assert(!m_could_reserve);
|
||||||
std::lock_guard<std::mutex> lock(m_wallet->mutexScanning);
|
if (m_wallet->fScanningWallet.exchange(true)) {
|
||||||
if (m_wallet->fScanningWallet) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_wallet->m_scanning_start = GetTimeMillis();
|
m_wallet->m_scanning_start = GetTimeMillis();
|
||||||
m_wallet->m_scanning_progress = 0;
|
m_wallet->m_scanning_progress = 0;
|
||||||
m_wallet->fScanningWallet = true;
|
|
||||||
m_could_reserve = true;
|
m_could_reserve = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1350,7 +1347,6 @@ public:
|
|||||||
|
|
||||||
~WalletRescanReserver()
|
~WalletRescanReserver()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_wallet->mutexScanning);
|
|
||||||
if (m_could_reserve) {
|
if (m_could_reserve) {
|
||||||
m_wallet->fScanningWallet = false;
|
m_wallet->fScanningWallet = false;
|
||||||
}
|
}
|
||||||
|
@ -14,8 +14,17 @@ class ConfArgsTest(BitcoinTestFramework):
|
|||||||
self.setup_clean_chain = True
|
self.setup_clean_chain = True
|
||||||
self.num_nodes = 1
|
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):
|
def run_test(self):
|
||||||
self.stop_node(0)
|
self.stop_node(0)
|
||||||
|
|
||||||
|
self.test_log_buffer()
|
||||||
|
|
||||||
# Remove the -datadir argument so it doesn't override the config file
|
# 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")]
|
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