5 #if defined(HAVE_CONFIG_H) 19 #include <system_error> 21 #include <type_traits> 22 #include <unordered_map> 26 #ifdef DEBUG_LOCKCONTENTION 27 #if !defined(HAVE_THREAD_LOCAL) 28 static_assert(
false,
"thread_local is not supported");
30 void PrintLockContention(
const char* pszName,
const char* pszFile,
int nLine)
32 LogPrintf(
"LOCKCONTENTION: %s\n", pszName);
33 LogPrintf(
"Locker: %s:%d\n", pszFile, nLine);
37 #ifdef DEBUG_LOCKORDER 49 struct CLockLocation {
55 const std::string& thread_name)
59 m_thread_name(thread_name),
65 "'%s' in %s:%s%s (in thread '%s')",
66 mutexName, sourceFile, sourceLine, (fTry ?
" (TRY)" :
""), m_thread_name);
69 std::string Name()
const 76 std::string mutexName;
77 std::string sourceFile;
78 const std::string& m_thread_name;
82 using LockStackItem = std::pair<void*, CLockLocation>;
83 using LockStack = std::vector<LockStackItem>;
84 using LockStacks = std::unordered_map<std::thread::id, LockStack>;
86 using LockPair = std::pair<void*, void*>;
87 using LockOrders = std::map<LockPair, LockStack>;
88 using InvLockOrders = std::set<LockPair>;
91 LockStacks m_lock_stacks;
92 LockOrders lockorders;
93 InvLockOrders invlockorders;
97 LockData& GetLockData() {
102 static LockData& lock_data = *
new LockData();
106 static void potential_deadlock_detected(
const LockPair& mismatch,
const LockStack& s1,
const LockStack& s2)
108 LogPrintf(
"POTENTIAL DEADLOCK DETECTED\n");
110 for (
const LockStackItem& i : s1) {
111 if (i.first == mismatch.first) {
114 if (i.first == mismatch.second) {
120 std::string mutex_a, mutex_b;
122 for (
const LockStackItem& i : s2) {
123 if (i.first == mismatch.first) {
125 mutex_a = i.second.Name();
127 if (i.first == mismatch.second) {
129 mutex_b = i.second.Name();
133 if (g_debug_lockorder_abort) {
134 tfm::format(std::cerr,
"Assertion failed: detected inconsistent lock order for %s, details in debug log.\n", s2.back().second.ToString());
137 throw std::logic_error(
strprintf(
"potential deadlock detected: %s -> %s -> %s", mutex_b, mutex_a, mutex_b));
140 static void double_lock_detected(
const void* mutex,
const LockStack& lock_stack)
144 for (
const LockStackItem& i : lock_stack) {
145 if (i.first == mutex) {
150 if (g_debug_lockorder_abort) {
152 "Assertion failed: detected double lock for %s, details in debug log.\n",
153 lock_stack.back().second.ToString());
156 throw std::logic_error(
"double lock detected");
159 template <
typename MutexType>
160 static void push_lock(MutexType* c,
const CLockLocation& locklocation)
162 constexpr
bool is_recursive_mutex =
163 std::is_base_of<RecursiveMutex, MutexType>::value ||
164 std::is_base_of<std::recursive_mutex, MutexType>::value;
166 LockData& lockdata = GetLockData();
167 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
169 LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
170 lock_stack.emplace_back(c, locklocation);
171 for (
size_t j = 0; j < lock_stack.size() - 1; ++j) {
172 const LockStackItem& i = lock_stack[j];
174 if (is_recursive_mutex) {
181 auto lock_stack_copy = lock_stack;
182 lock_stack.pop_back();
183 double_lock_detected(c, lock_stack_copy);
187 const LockPair p1 = std::make_pair(i.first, c);
188 if (lockdata.lockorders.count(p1))
191 const LockPair p2 = std::make_pair(c, i.first);
192 if (lockdata.lockorders.count(p2)) {
193 auto lock_stack_copy = lock_stack;
194 lock_stack.pop_back();
195 potential_deadlock_detected(p1, lockdata.lockorders[p2], lock_stack_copy);
199 lockdata.lockorders.emplace(p1, lock_stack);
200 lockdata.invlockorders.insert(p2);
204 static void pop_lock()
206 LockData& lockdata = GetLockData();
207 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
209 LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
210 lock_stack.pop_back();
211 if (lock_stack.empty()) {
212 lockdata.m_lock_stacks.erase(std::this_thread::get_id());
216 template <
typename MutexType>
217 void EnterCritical(
const char* pszName,
const char* pszFile,
int nLine, MutexType*
cs,
bool fTry)
223 template void EnterCritical(
const char*,
const char*,
int, std::mutex*,
bool);
224 template void EnterCritical(
const char*,
const char*,
int, std::recursive_mutex*,
bool);
226 void CheckLastCritical(
void*
cs, std::string& lockname,
const char* guardname,
const char* file,
int line)
228 LockData& lockdata = GetLockData();
229 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
231 const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
232 if (!lock_stack.empty()) {
233 const auto& lastlock = lock_stack.back();
234 if (lastlock.first ==
cs) {
235 lockname = lastlock.second.Name();
240 LogPrintf(
"INCONSISTENT LOCK ORDER DETECTED\n");
241 LogPrintf(
"Current lock order (least recent first) is:\n");
242 for (
const LockStackItem& i : lock_stack) {
245 if (g_debug_lockorder_abort) {
246 tfm::format(std::cerr,
"%s:%s %s was not most recent critical section locked, details in debug log.\n", file, line, guardname);
249 throw std::logic_error(
strprintf(
"%s was not most recent critical section locked", guardname));
257 std::string LocksHeld()
259 LockData& lockdata = GetLockData();
260 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
262 const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
264 for (
const LockStackItem& i : lock_stack)
265 result += i.second.ToString() + std::string(
"\n");
269 static bool LockHeld(
void* mutex)
271 LockData& lockdata = GetLockData();
272 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
274 const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
275 for (
const LockStackItem& i : lock_stack) {
276 if (i.first == mutex)
return true;
282 template <
typename MutexType>
285 if (LockHeld(
cs))
return;
286 tfm::format(std::cerr,
"Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
292 template <
typename MutexType>
295 if (!LockHeld(
cs))
return;
296 tfm::format(std::cerr,
"Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
304 LockData& lockdata = GetLockData();
305 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
306 const LockPair item = std::make_pair(
cs,
nullptr);
307 LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
308 while (it != lockdata.lockorders.end() && it->first.first ==
cs) {
309 const LockPair invitem = std::make_pair(it->first.second, it->first.first);
310 lockdata.invlockorders.erase(invitem);
311 lockdata.lockorders.erase(it++);
313 InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item);
314 while (invit != lockdata.invlockorders.end() && invit->first ==
cs) {
315 const LockPair invinvitem = std::make_pair(invit->second, invit->first);
316 lockdata.lockorders.erase(invinvitem);
317 lockdata.invlockorders.erase(invit++);
323 LockData& lockdata = GetLockData();
324 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
325 const auto it = lockdata.m_lock_stacks.find(std::this_thread::get_id());
326 if (it == lockdata.m_lock_stacks.end()) {
329 return it->second.empty();
332 bool g_debug_lockorder_abort =
true;
void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) LOCKS_EXCLUDED(cs)
void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line)
std::string ToString(const T &t)
Locale-independent version of std::to_string.
void DeleteLock(void *cs)
void EnterCritical(const char *pszName, const char *pszFile, int nLine, MutexType *cs, bool fTry=false)
void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) EXCLUSIVE_LOCKS_REQUIRED(cs)
const std::string & ThreadGetInternalName()
Get the thread's internal (in-memory) name; used e.g.