17#include <system_error>
20#include <unordered_map>
38 CLockLocation(
const char *pszName,
const char *pszFile,
int nLine,
39 bool fTryIn,
const std::string &thread_name)
40 : fTry(fTryIn), mutexName(pszName), sourceFile(pszFile),
41 m_thread_name(thread_name), sourceLine(nLine) {}
44 return strprintf(
"'%s' in %s:%s%s (in thread '%s')", mutexName,
45 sourceFile, sourceLine, (fTry ?
" (TRY)" :
""),
49 std::string Name()
const {
return mutexName; }
53 std::string mutexName;
54 std::string sourceFile;
55 const std::string &m_thread_name;
59using LockStackItem = std::pair<void *, CLockLocation>;
60using LockStack = std::vector<LockStackItem>;
61using LockStacks = std::unordered_map<std::thread::id, LockStack>;
63using LockPair = std::pair<void *, void *>;
64using LockOrders = std::map<LockPair, LockStack>;
65using InvLockOrders = std::set<LockPair>;
68 LockStacks m_lock_stacks;
69 LockOrders lockorders;
70 InvLockOrders invlockorders;
74LockData &GetLockData() {
80 static LockData &lock_data = *
new LockData();
84static void potential_deadlock_detected(
const LockPair &mismatch,
86 const LockStack &s2) {
87 LogPrintf(
"POTENTIAL DEADLOCK DETECTED\n");
89 for (
const LockStackItem &i : s1) {
90 if (i.first == mismatch.first) {
93 if (i.first == mismatch.second) {
99 std::string mutex_a, mutex_b;
101 for (
const LockStackItem &i : s2) {
102 if (i.first == mismatch.first) {
104 mutex_a = i.second.Name();
106 if (i.first == mismatch.second) {
108 mutex_b = i.second.Name();
112 if (g_debug_lockorder_abort) {
115 "Assertion failed: detected inconsistent lock order for %s, "
116 "details in debug log.\n",
117 s2.back().second.ToString());
120 throw std::logic_error(
121 strprintf(
"potential deadlock detected: %s -> %s -> %s", mutex_b,
125static void double_lock_detected(
const void *mutex,
126 const LockStack &lock_stack) {
129 for (
const LockStackItem &i : lock_stack) {
130 if (i.first == mutex) {
135 if (g_debug_lockorder_abort) {
137 "Assertion failed: detected double lock for %s, details in "
139 lock_stack.back().second.ToString());
142 throw std::logic_error(
"double lock detected");
145template <
typename MutexType>
146static void push_lock(MutexType *c,
const CLockLocation &locklocation) {
147 constexpr bool is_recursive_mutex =
148 std::is_base_of<RecursiveMutex, MutexType>::value ||
149 std::is_base_of<std::recursive_mutex, MutexType>::value;
151 LockData &lockdata = GetLockData();
152 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
154 LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
155 lock_stack.emplace_back(c, locklocation);
156 for (
size_t j = 0; j < lock_stack.size() - 1; ++j) {
157 const LockStackItem &i = lock_stack[j];
159 if (is_recursive_mutex) {
167 auto lock_stack_copy = lock_stack;
168 lock_stack.pop_back();
169 double_lock_detected(c, lock_stack_copy);
173 const LockPair p1 = std::make_pair(i.first, c);
174 if (lockdata.lockorders.count(p1)) {
178 const LockPair p2 = std::make_pair(c, i.first);
179 if (lockdata.lockorders.count(p2)) {
180 auto lock_stack_copy = lock_stack;
181 lock_stack.pop_back();
182 potential_deadlock_detected(p1, lockdata.lockorders[p2],
187 lockdata.lockorders.emplace(p1, lock_stack);
188 lockdata.invlockorders.insert(p2);
192static void pop_lock() {
193 LockData &lockdata = GetLockData();
194 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
196 LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
197 lock_stack.pop_back();
198 if (lock_stack.empty()) {
199 lockdata.m_lock_stacks.erase(std::this_thread::get_id());
203template <
typename MutexType>
204void EnterCritical(
const char *pszName,
const char *pszFile,
int nLine,
205 MutexType *
cs,
bool fTry) {
206 push_lock(
cs, CLockLocation(pszName, pszFile, nLine, fTry,
212template void EnterCritical(
const char *,
const char *,
int, std::mutex *,
215 std::recursive_mutex *,
bool);
218 const char *file,
int line) {
219 LockData &lockdata = GetLockData();
220 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
222 const LockStack &lock_stack =
223 lockdata.m_lock_stacks[std::this_thread::get_id()];
224 if (!lock_stack.empty()) {
225 const auto &lastlock = lock_stack.back();
226 if (lastlock.first ==
cs) {
227 lockname = lastlock.second.Name();
232 LogPrintf(
"INCONSISTENT LOCK ORDER DETECTED\n");
233 LogPrintf(
"Current lock order (least recent first) is:\n");
234 for (
const LockStackItem &i : lock_stack) {
237 if (g_debug_lockorder_abort) {
239 "%s:%s %s was not most recent critical section locked, "
240 "details in debug log.\n",
241 file, line, guardname);
244 throw std::logic_error(
245 strprintf(
"%s was not most recent critical section locked", guardname));
252std::string LocksHeld() {
253 LockData &lockdata = GetLockData();
254 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
256 const LockStack &lock_stack =
257 lockdata.m_lock_stacks[std::this_thread::get_id()];
259 for (
const LockStackItem &i : lock_stack) {
260 result += i.second.ToString() + std::string(
"\n");
265static bool LockHeld(
void *mutex) {
266 LockData &lockdata = GetLockData();
267 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
269 const LockStack &lock_stack =
270 lockdata.m_lock_stacks[std::this_thread::get_id()];
271 for (
const LockStackItem &i : lock_stack) {
272 if (i.first == mutex) {
280template <
typename MutexType>
287 "Assertion failed: lock %s not held in %s:%i; locks held:\n%s",
288 pszName, pszFile, nLine, LocksHeld());
295template <
typename MutexType>
297 int nLine, MutexType *
cs) {
302 "Assertion failed: lock %s held in %s:%i; locks held:\n%s",
303 pszName, pszFile, nLine, LocksHeld());
312 LockData &lockdata = GetLockData();
313 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
314 const LockPair item = std::make_pair(
cs,
nullptr);
315 LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
316 while (it != lockdata.lockorders.end() && it->first.first ==
cs) {
317 const LockPair invitem =
318 std::make_pair(it->first.second, it->first.first);
319 lockdata.invlockorders.erase(invitem);
320 lockdata.lockorders.erase(it++);
322 InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item);
323 while (invit != lockdata.invlockorders.end() && invit->first ==
cs) {
324 const LockPair invinvitem = std::make_pair(invit->second, invit->first);
325 lockdata.lockorders.erase(invinvitem);
326 lockdata.invlockorders.erase(invit++);
331 LockData &lockdata = GetLockData();
332 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
333 const auto it = lockdata.m_lock_stacks.find(std::this_thread::get_id());
334 if (it == lockdata.m_lock_stacks.end()) {
337 return it->second.empty();
340bool g_debug_lockorder_abort =
true;
#define LogPrintfToBeContinued
These are aliases used to explicitly state that the message should not end with a newline character.
const std::string & ThreadGetInternalName()
Get the thread's internal (in-memory) name; used e.g.
std::string ToString(const T &t)
Locale-independent version of std::to_string.
void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) EXCLUSIVE_LOCKS_REQUIRED(cs)
void EnterCritical(const char *pszName, const char *pszFile, int nLine, MutexType *cs, bool fTry=false)
void DeleteLock(void *cs)
void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line)
void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) LOCKS_EXCLUDED(cs)