5 #if defined(HAVE_CONFIG_H)
18 #include <system_error>
20 #include <unordered_map>
24 #ifdef DEBUG_LOCKCONTENTION
25 #if !defined(HAVE_THREAD_LOCAL)
26 static_assert(
false,
"thread_local is not supported");
28 void PrintLockContention(
const char* pszName,
const char* pszFile,
int nLine)
30 LogPrintf(
"LOCKCONTENTION: %s\n", pszName);
31 LogPrintf(
"Locker: %s:%d\n", pszFile, nLine);
35 #ifdef DEBUG_LOCKORDER
47 struct CLockLocation {
53 const std::string& thread_name)
57 m_thread_name(thread_name),
63 "'%s' in %s:%s%s (in thread '%s')",
64 mutexName, sourceFile, sourceLine, (fTry ?
" (TRY)" :
""), m_thread_name);
67 std::string Name()
const
74 std::string mutexName;
75 std::string sourceFile;
76 const std::string& m_thread_name;
80 using LockStackItem = std::pair<void*, CLockLocation>;
81 using LockStack = std::vector<LockStackItem>;
82 using LockStacks = std::unordered_map<std::thread::id, LockStack>;
84 using LockPair = std::pair<void*, void*>;
85 using LockOrders = std::map<LockPair, LockStack>;
86 using InvLockOrders = std::set<LockPair>;
89 LockStacks m_lock_stacks;
90 LockOrders lockorders;
91 InvLockOrders invlockorders;
95 LockData& GetLockData() {
100 static LockData& lock_data = *
new LockData();
104 static void potential_deadlock_detected(
const LockPair& mismatch,
const LockStack& s1,
const LockStack& s2)
106 LogPrintf(
"POTENTIAL DEADLOCK DETECTED\n");
108 for (
const LockStackItem& i : s1) {
109 if (i.first == mismatch.first) {
112 if (i.first == mismatch.second) {
118 std::string mutex_a, mutex_b;
120 for (
const LockStackItem& i : s2) {
121 if (i.first == mismatch.first) {
123 mutex_a = i.second.Name();
125 if (i.first == mismatch.second) {
127 mutex_b = i.second.Name();
131 if (g_debug_lockorder_abort) {
132 tfm::format(std::cerr,
"Assertion failed: detected inconsistent lock order for %s, details in debug log.\n", s2.back().second.ToString());
135 throw std::logic_error(
strprintf(
"potential deadlock detected: %s -> %s -> %s", mutex_b, mutex_a, mutex_b));
138 static void push_lock(
void* c,
const CLockLocation& locklocation)
140 LockData& lockdata = GetLockData();
141 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
143 LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
144 lock_stack.emplace_back(c, locklocation);
145 for (
const LockStackItem& i : lock_stack) {
149 const LockPair p1 = std::make_pair(i.first, c);
150 if (lockdata.lockorders.count(p1))
153 const LockPair p2 = std::make_pair(c, i.first);
154 if (lockdata.lockorders.count(p2)) {
155 auto lock_stack_copy = lock_stack;
156 lock_stack.pop_back();
157 potential_deadlock_detected(p1, lockdata.lockorders[p2], lock_stack_copy);
161 lockdata.lockorders.emplace(p1, lock_stack);
162 lockdata.invlockorders.insert(p2);
166 static void pop_lock()
168 LockData& lockdata = GetLockData();
169 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
171 LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
172 lock_stack.pop_back();
173 if (lock_stack.empty()) {
174 lockdata.m_lock_stacks.erase(std::this_thread::get_id());
178 void EnterCritical(
const char* pszName,
const char* pszFile,
int nLine,
void*
cs,
bool fTry)
183 void CheckLastCritical(
void* cs, std::string& lockname,
const char* guardname,
const char* file,
int line)
186 LockData& lockdata = GetLockData();
187 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
189 const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
190 if (!lock_stack.empty()) {
191 const auto& lastlock = lock_stack.back();
192 if (lastlock.first == cs) {
193 lockname = lastlock.second.Name();
198 throw std::system_error(EPERM, std::generic_category(),
strprintf(
"%s:%s %s was not most recent critical section locked", file, line, guardname));
206 std::string LocksHeld()
208 LockData& lockdata = GetLockData();
209 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
211 const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
213 for (
const LockStackItem& i : lock_stack)
214 result += i.second.ToString() + std::string(
"\n");
218 static bool LockHeld(
void* mutex)
220 LockData& lockdata = GetLockData();
221 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
223 const LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
224 for (
const LockStackItem& i : lock_stack) {
225 if (i.first == mutex)
return true;
231 template <
typename MutexType>
234 if (LockHeld(cs))
return;
235 tfm::format(std::cerr,
"Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
241 template <
typename MutexType>
244 if (!LockHeld(cs))
return;
245 tfm::format(std::cerr,
"Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
253 LockData& lockdata = GetLockData();
254 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
255 const LockPair item = std::make_pair(cs,
nullptr);
256 LockOrders::iterator
it = lockdata.lockorders.lower_bound(item);
257 while (it != lockdata.lockorders.end() && it->first.first ==
cs) {
258 const LockPair invitem = std::make_pair(it->first.second, it->first.first);
259 lockdata.invlockorders.erase(invitem);
260 lockdata.lockorders.erase(it++);
262 InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item);
263 while (invit != lockdata.invlockorders.end() && invit->first ==
cs) {
264 const LockPair invinvitem = std::make_pair(invit->second, invit->first);
265 lockdata.lockorders.erase(invinvitem);
266 lockdata.invlockorders.erase(invit++);
272 LockData& lockdata = GetLockData();
273 std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
274 const auto it = lockdata.m_lock_stacks.find(std::this_thread::get_id());
275 if (it == lockdata.m_lock_stacks.end()) {
278 return it->second.empty();
281 bool g_debug_lockorder_abort =
true;
void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) EXCLUSIVE_LOCKS_REQUIRED(!cs)
static void LogPrintf(const char *fmt, const Args &...args)
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 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, void *cs, bool fTry=false)
const std::string & ThreadGetInternalName()
Get the thread's internal (in-memory) name; used e.g.