16 #include <system_error>
18 #include <unordered_map>
22 #ifdef DEBUG_LOCKORDER
35 struct CLockLocation {
36 CLockLocation(
const char *pszName,
const char *pszFile,
int nLine,
37 bool fTryIn,
const std::string &thread_name)
38 : fTry(fTryIn), mutexName(pszName), sourceFile(pszFile),
39 m_thread_name(thread_name), sourceLine(nLine) {}
42 return strprintf(
"'%s' in %s:%s%s (in thread '%s')", mutexName,
43 sourceFile, sourceLine, (fTry ?
" (TRY)" :
""),
47 std::string Name()
const {
return mutexName; }
51 std::string mutexName;
52 std::string sourceFile;
53 const std::string &m_thread_name;
57 using LockStackItem = std::pair<void *, CLockLocation>;
58 using LockStack = std::vector<LockStackItem>;
59 using LockStacks = std::unordered_map<std::thread::id, LockStack>;
61 using LockPair = std::pair<void *, void *>;
62 using LockOrders = std::map<LockPair, LockStack>;
63 using InvLockOrders = std::set<LockPair>;
66 LockStacks m_lock_stacks;
67 LockOrders lockorders;
68 InvLockOrders invlockorders;
72 LockData &GetLockData() {
78 static LockData &lock_data = *
new LockData();
82 static void potential_deadlock_detected(
const LockPair &mismatch,
84 const LockStack &s2) {
85 LogPrintf(
"POTENTIAL DEADLOCK DETECTED\n");
87 for (
const LockStackItem &i : s1) {
88 if (i.first == mismatch.first) {
91 if (i.first == mismatch.second) {
97 std::string mutex_a, mutex_b;
99 for (
const LockStackItem &i : s2) {
100 if (i.first == mismatch.first) {
102 mutex_a = i.second.Name();
104 if (i.first == mismatch.second) {
106 mutex_b = i.second.Name();
110 if (g_debug_lockorder_abort) {
113 "Assertion failed: detected inconsistent lock order for %s, "
114 "details in debug log.\n",
115 s2.back().second.ToString());
118 throw std::logic_error(
119 strprintf(
"potential deadlock detected: %s -> %s -> %s", mutex_b,
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)) {
139 const LockPair p2 = std::make_pair(c, i.first);
140 if (lockdata.lockorders.count(p2)) {
141 auto lock_stack_copy = lock_stack;
142 lock_stack.pop_back();
143 potential_deadlock_detected(p1, lockdata.lockorders[p2],
148 lockdata.lockorders.emplace(p1, lock_stack);
149 lockdata.invlockorders.insert(p2);
153 static void pop_lock() {
154 LockData &lockdata = GetLockData();
155 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
157 LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
158 lock_stack.pop_back();
159 if (lock_stack.empty()) {
160 lockdata.m_lock_stacks.erase(std::this_thread::get_id());
164 void EnterCritical(
const char *pszName,
const char *pszFile,
int nLine,
165 void *
cs,
bool fTry) {
166 push_lock(
cs, CLockLocation(pszName, pszFile, nLine, fTry,
171 const char *file,
int line) {
172 LockData &lockdata = GetLockData();
173 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
175 const LockStack &lock_stack =
176 lockdata.m_lock_stacks[std::this_thread::get_id()];
177 if (!lock_stack.empty()) {
178 const auto &lastlock = lock_stack.back();
179 if (lastlock.first ==
cs) {
180 lockname = lastlock.second.Name();
185 LogPrintf(
"INCONSISTENT LOCK ORDER DETECTED\n");
186 LogPrintf(
"Current lock order (least recent first) is:\n");
187 for (
const LockStackItem &i : lock_stack) {
190 if (g_debug_lockorder_abort) {
192 "%s:%s %s was not most recent critical section locked, "
193 "details in debug log.\n",
194 file, line, guardname);
197 throw std::logic_error(
198 strprintf(
"%s was not most recent critical section locked", guardname));
205 std::string LocksHeld() {
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()];
212 for (
const LockStackItem &i : lock_stack) {
213 result += i.second.ToString() + std::string(
"\n");
218 static bool LockHeld(
void *mutex) {
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 for (
const LockStackItem &i : lock_stack) {
225 if (i.first == mutex) {
233 template <
typename MutexType>
240 "Assertion failed: lock %s not held in %s:%i; locks held:\n%s",
241 pszName, pszFile, nLine, LocksHeld());
248 template <
typename MutexType>
250 int nLine, MutexType *
cs) {
255 "Assertion failed: lock %s held in %s:%i; locks held:\n%s",
256 pszName, pszFile, nLine, LocksHeld());
265 LockData &lockdata = GetLockData();
266 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
267 const LockPair item = std::make_pair(
cs,
nullptr);
268 LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
269 while (it != lockdata.lockorders.end() && it->first.first ==
cs) {
270 const LockPair invitem =
271 std::make_pair(it->first.second, it->first.first);
272 lockdata.invlockorders.erase(invitem);
273 lockdata.lockorders.erase(it++);
275 InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item);
276 while (invit != lockdata.invlockorders.end() && invit->first ==
cs) {
277 const LockPair invinvitem = std::make_pair(invit->second, invit->first);
278 lockdata.lockorders.erase(invinvitem);
279 lockdata.invlockorders.erase(invit++);
284 LockData &lockdata = GetLockData();
285 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
286 const auto it = lockdata.m_lock_stacks.find(std::this_thread::get_id());
287 if (it == lockdata.m_lock_stacks.end()) {
290 return it->second.empty();
293 bool 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 DeleteLock(void *cs)
void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line)
void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry=false)
void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) LOCKS_EXCLUDED(cs)