|
13 | 13 | #include <util/strencodings.h>
|
14 | 14 | #include <util/threadnames.h>
|
15 | 15 |
|
| 16 | +#include <boost/thread/mutex.hpp> |
| 17 | + |
16 | 18 | #include <map>
|
| 19 | +#include <mutex> |
17 | 20 | #include <set>
|
18 | 21 | #include <system_error>
|
19 | 22 | #include <thread>
|
| 23 | +#include <type_traits> |
20 | 24 | #include <unordered_map>
|
21 | 25 | #include <utility>
|
22 | 26 | #include <vector>
|
@@ -135,16 +139,50 @@ static void potential_deadlock_detected(const LockPair& mismatch, const LockStac
|
135 | 139 | throw std::logic_error(strprintf("potential deadlock detected: %s -> %s -> %s", mutex_b, mutex_a, mutex_b));
|
136 | 140 | }
|
137 | 141 |
|
138 |
| -static void push_lock(void* c, const CLockLocation& locklocation) |
| 142 | +static void double_lock_detected(const void* mutex, LockStack& lock_stack) |
139 | 143 | {
|
| 144 | + LogPrintf("DOUBLE LOCK DETECTED\n"); |
| 145 | + LogPrintf("Lock order:\n"); |
| 146 | + for (const LockStackItem& i : lock_stack) { |
| 147 | + if (i.first == mutex) { |
| 148 | + LogPrintf(" (*)"); /* Continued */ |
| 149 | + } |
| 150 | + LogPrintf(" %s\n", i.second.ToString()); |
| 151 | + } |
| 152 | + if (g_debug_lockorder_abort) { |
| 153 | + tfm::format(std::cerr, "Assertion failed: detected double lock at %s:%i, details in debug log.\n", __FILE__, __LINE__); |
| 154 | + abort(); |
| 155 | + } |
| 156 | + throw std::logic_error("double lock detected"); |
| 157 | +} |
| 158 | + |
| 159 | +template <typename MutexType> |
| 160 | +static void push_lock(MutexType* c, const CLockLocation& locklocation) |
| 161 | +{ |
| 162 | + constexpr bool is_recursive_mutex = |
| 163 | + std::is_base_of<RecursiveMutex, MutexType>::value || |
| 164 | + std::is_base_of<std::recursive_mutex, MutexType>::value; |
| 165 | + |
140 | 166 | LockData& lockdata = GetLockData();
|
141 | 167 | std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
|
142 | 168 |
|
143 | 169 | LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
|
144 | 170 | lock_stack.emplace_back(c, locklocation);
|
145 |
| - for (const LockStackItem& i : lock_stack) { |
146 |
| - if (i.first == c) |
147 |
| - break; |
| 171 | + for (size_t j = 0; j < lock_stack.size() - 1; ++j) { |
| 172 | + const LockStackItem& i = lock_stack[j]; |
| 173 | + if (i.first == c) { |
| 174 | + if (is_recursive_mutex) { |
| 175 | + break; |
| 176 | + } |
| 177 | + // It is not a recursive mutex and it appears in the stack two times: |
| 178 | + // at position `j` and at the end (which we added just before this loop). |
| 179 | + // Can't allow locking the same (non-recursive) mutex two times from the |
| 180 | + // same thread as that results in an undefined behavior. |
| 181 | + auto lock_stack_copy = lock_stack; |
| 182 | + lock_stack.pop_back(); |
| 183 | + double_lock_detected(c, lock_stack_copy); |
| 184 | + // double_lock_detected() does not return. |
| 185 | + } |
148 | 186 |
|
149 | 187 | const LockPair p1 = std::make_pair(i.first, c);
|
150 | 188 | if (lockdata.lockorders.count(p1))
|
@@ -175,10 +213,16 @@ static void pop_lock()
|
175 | 213 | }
|
176 | 214 | }
|
177 | 215 |
|
178 |
| -void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) |
| 216 | +template <typename MutexType> |
| 217 | +void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry) |
179 | 218 | {
|
180 | 219 | push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName()));
|
181 | 220 | }
|
| 221 | +template void EnterCritical(const char*, const char*, int, Mutex*, bool); |
| 222 | +template void EnterCritical(const char*, const char*, int, RecursiveMutex*, bool); |
| 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); |
| 225 | +template void EnterCritical(const char*, const char*, int, boost::mutex*, bool); |
182 | 226 |
|
183 | 227 | void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line)
|
184 | 228 | {
|
|
0 commit comments