aboutsummaryrefslogtreecommitdiff
path: root/src/sync.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sync.cpp')
-rw-r--r--src/sync.cpp78
1 files changed, 59 insertions, 19 deletions
diff --git a/src/sync.cpp b/src/sync.cpp
index 255eb4f00..71657a743 100644
--- a/src/sync.cpp
+++ b/src/sync.cpp
@@ -1,16 +1,20 @@
-// Copyright (c) 2011-2018 The Bitcoin Core developers
+// Copyright (c) 2011-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
#include <sync.h>
+#include <tinyformat.h>
#include <logging.h>
-#include <utilstrencodings.h>
-
-#include <stdio.h>
+#include <util/strencodings.h>
+#include <util/threadnames.h>
+#include <system_error>
#include <map>
-#include <memory>
#include <set>
#ifdef DEBUG_LOCKCONTENTION
@@ -37,23 +41,35 @@ void PrintLockContention(const char* pszName, const char* pszFile, int nLine)
//
struct CLockLocation {
- CLockLocation(const char* pszName, const char* pszFile, int nLine, bool fTryIn)
+ CLockLocation(
+ const char* pszName,
+ const char* pszFile,
+ int nLine,
+ bool fTryIn,
+ const std::string& thread_name)
+ : fTry(fTryIn),
+ mutexName(pszName),
+ sourceFile(pszFile),
+ m_thread_name(thread_name),
+ sourceLine(nLine) {}
+
+ std::string ToString() const
{
- mutexName = pszName;
- sourceFile = pszFile;
- sourceLine = nLine;
- fTry = fTryIn;
+ return strprintf(
+ "%s %s:%s%s (in thread %s)",
+ mutexName, sourceFile, itostr(sourceLine), (fTry ? " (TRY)" : ""), m_thread_name);
}
- std::string ToString() const
+ std::string Name() const
{
- return mutexName + " " + sourceFile + ":" + itostr(sourceLine) + (fTry ? " (TRY)" : "");
+ return mutexName;
}
private:
bool fTry;
std::string mutexName;
std::string sourceFile;
+ const std::string& m_thread_name;
int sourceLine;
};
@@ -64,7 +80,7 @@ typedef std::set<std::pair<void*, void*> > InvLockOrders;
struct LockData {
// Very ugly hack: as the global constructs and destructors run single
// threaded, we use this boolean to know whether LockData still exists,
- // as DeleteLock can get called by global CCriticalSection destructors
+ // as DeleteLock can get called by global RecursiveMutex destructors
// after LockData disappears.
bool available;
LockData() : available(true) {}
@@ -73,7 +89,11 @@ struct LockData {
LockOrders lockorders;
InvLockOrders invlockorders;
std::mutex dd_mutex;
-} static lockdata;
+};
+LockData& GetLockData() {
+ static LockData lockdata;
+ return lockdata;
+}
static thread_local LockStack g_lockstack;
@@ -100,11 +120,16 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch,
}
LogPrintf(" %s\n", i.second.ToString());
}
- assert(false);
+ if (g_debug_lockorder_abort) {
+ tfm::format(std::cerr, "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)
{
+ LockData& lockdata = GetLockData();
std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
g_lockstack.push_back(std::make_pair(c, locklocation));
@@ -116,7 +141,7 @@ static void push_lock(void* c, const CLockLocation& locklocation)
std::pair<void*, void*> p1 = std::make_pair(i.first, c);
if (lockdata.lockorders.count(p1))
continue;
- lockdata.lockorders[p1] = g_lockstack;
+ lockdata.lockorders.emplace(p1, g_lockstack);
std::pair<void*, void*> p2 = std::make_pair(c, i.first);
lockdata.invlockorders.insert(p2);
@@ -132,7 +157,19 @@ static void pop_lock()
void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
{
- push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry));
+ push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName()));
+}
+
+void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line)
+{
+ if (!g_lockstack.empty()) {
+ const auto& lastlock = g_lockstack.back();
+ if (lastlock.first == cs) {
+ lockname = lastlock.second.Name();
+ return;
+ }
+ }
+ throw std::system_error(EPERM, std::generic_category(), strprintf("%s:%s %s was not most recent critical section locked", file, line, guardname));
}
void LeaveCritical()
@@ -153,7 +190,7 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine,
for (const std::pair<void*, CLockLocation>& i : g_lockstack)
if (i.first == cs)
return;
- fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
+ tfm::format(std::cerr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
abort();
}
@@ -161,7 +198,7 @@ void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLi
{
for (const std::pair<void*, CLockLocation>& i : g_lockstack) {
if (i.first == cs) {
- fprintf(stderr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
+ tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
abort();
}
}
@@ -169,6 +206,7 @@ void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLi
void DeleteLock(void* cs)
{
+ LockData& lockdata = GetLockData();
if (!lockdata.available) {
// We're already shutting down.
return;
@@ -189,4 +227,6 @@ void DeleteLock(void* cs)
}
}
+bool g_debug_lockorder_abort = true;
+
#endif /* DEBUG_LOCKORDER */