16 #include <system_error> 18 #include <unordered_map> 22 #ifdef DEBUG_LOCKCONTENTION 23 void PrintLockContention(
const char *pszName,
const char *pszFile,
int nLine) {
24 LogPrintf(
"LOCKCONTENTION: %s\n", pszName);
25 LogPrintf(
"Locker: %s:%d\n", pszFile, nLine);
29 #ifdef DEBUG_LOCKORDER 42 struct CLockLocation {
43 CLockLocation(
const char *pszName,
const char *pszFile,
int nLine,
44 bool fTryIn,
const std::string &thread_name)
45 : fTry(fTryIn), mutexName(pszName), sourceFile(pszFile),
46 m_thread_name(thread_name), sourceLine(nLine) {}
49 return strprintf(
"%s %s:%s%s (in thread %s)", mutexName, sourceFile,
50 sourceLine, (fTry ?
" (TRY)" :
""), m_thread_name);
53 std::string Name()
const {
return mutexName; }
57 std::string mutexName;
58 std::string sourceFile;
59 const std::string &m_thread_name;
63 using LockStackItem = std::pair<void *, CLockLocation>;
64 using LockStack = std::vector<LockStackItem>;
65 using LockStacks = std::unordered_map<std::thread::id, LockStack>;
67 using LockPair = std::pair<void *, void *>;
68 using LockOrders = std::map<LockPair, LockStack>;
69 using InvLockOrders = std::set<LockPair>;
72 LockStacks m_lock_stacks;
73 LockOrders lockorders;
74 InvLockOrders invlockorders;
78 LockData &GetLockData() {
84 static LockData &lock_data = *
new LockData();
88 static void potential_deadlock_detected(
const LockPair &mismatch,
90 const LockStack &s2) {
91 LogPrintf(
"POTENTIAL DEADLOCK DETECTED\n");
93 for (
const LockStackItem &i : s2) {
94 if (i.first == mismatch.first) {
97 if (i.first == mismatch.second) {
103 for (
const LockStackItem &i : s1) {
104 if (i.first == mismatch.first) {
107 if (i.first == mismatch.second) {
112 if (g_debug_lockorder_abort) {
115 "Assertion failed: detected inconsistent lock order at %s:%i, " 116 "details in debug log.\n",
120 throw std::logic_error(
"potential deadlock detected");
123 static void push_lock(
void *c,
const CLockLocation &locklocation) {
124 LockData &lockdata = GetLockData();
125 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
127 LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
128 lock_stack.emplace_back(c, locklocation);
129 for (
const LockStackItem &i : lock_stack) {
134 const LockPair p1 = std::make_pair(i.first, c);
135 if (lockdata.lockorders.count(p1)) {
138 lockdata.lockorders.emplace(p1, lock_stack);
140 const LockPair p2 = std::make_pair(c, i.first);
141 lockdata.invlockorders.insert(p2);
142 if (lockdata.lockorders.count(p2)) {
143 potential_deadlock_detected(p1, lockdata.lockorders[p2],
144 lockdata.lockorders[p1]);
149 static void pop_lock() {
150 LockData &lockdata = GetLockData();
151 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
153 LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
154 lock_stack.pop_back();
155 if (lock_stack.empty()) {
156 lockdata.m_lock_stacks.erase(std::this_thread::get_id());
160 void EnterCritical(
const char *pszName,
const char *pszFile,
int nLine,
161 void *
cs,
bool fTry) {
162 push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry,
167 const char *file,
int line) {
169 LockData &lockdata = GetLockData();
170 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
172 const LockStack &lock_stack =
173 lockdata.m_lock_stacks[std::this_thread::get_id()];
174 if (!lock_stack.empty()) {
175 const auto &lastlock = lock_stack.back();
176 if (lastlock.first == cs) {
177 lockname = lastlock.second.Name();
182 throw std::system_error(
183 EPERM, std::generic_category(),
184 strprintf(
"%s:%s %s was not most recent critical section locked", file,
192 std::string LocksHeld() {
193 LockData &lockdata = GetLockData();
194 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
196 const LockStack &lock_stack =
197 lockdata.m_lock_stacks[std::this_thread::get_id()];
199 for (
const LockStackItem &i : lock_stack) {
200 result += i.second.ToString() + std::string(
"\n");
205 static bool LockHeld(
void *mutex) {
206 LockData &lockdata = GetLockData();
207 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
209 const LockStack &lock_stack =
210 lockdata.m_lock_stacks[std::this_thread::get_id()];
211 for (
const LockStackItem &i : lock_stack) {
212 if (i.first == mutex) {
220 template <
typename MutexType>
227 "Assertion failed: lock %s not held in %s:%i; locks held:\n%s",
228 pszName, pszFile, nLine, LocksHeld());
236 int nLine,
void *cs) {
241 "Assertion failed: lock %s held in %s:%i; locks held:\n%s",
242 pszName, pszFile, nLine, LocksHeld());
247 LockData &lockdata = GetLockData();
248 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
249 const LockPair item = std::make_pair(cs,
nullptr);
250 LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
251 while (it != lockdata.lockorders.end() && it->first.first ==
cs) {
252 const LockPair invitem =
253 std::make_pair(it->first.second, it->first.first);
254 lockdata.invlockorders.erase(invitem);
255 lockdata.lockorders.erase(it++);
257 InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item);
258 while (invit != lockdata.invlockorders.end() && invit->first ==
cs) {
259 const LockPair invinvitem = std::make_pair(invit->second, invit->first);
260 lockdata.lockorders.erase(invinvitem);
261 lockdata.invlockorders.erase(invit++);
265 bool g_debug_lockorder_abort =
true;
static void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, void *cs)
#define LogPrintfToBeContinued
These are aliases used to explicitly state that the message should not end with a newline character...
static void LogPrintf(const char *fmt, const Args &... args)
static void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) ASSERT_EXCLUSIVE_LOCK(cs)
std::string ToString(const T &t)
Locale-independent version of std::to_string.
static void DeleteLock(void *cs)
static void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line)
static void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry=false)
static void LeaveCritical()
const std::string & ThreadGetInternalName()
Get the thread's internal (in-memory) name; used e.g.