|
20 | 20 | #include <set>
|
21 | 21 | #include <system_error>
|
22 | 22 | #include <thread>
|
| 23 | +#include <type_traits> |
23 | 24 | #include <unordered_map>
|
24 | 25 | #include <utility>
|
25 | 26 | #include <vector>
|
@@ -138,17 +139,50 @@ static void potential_deadlock_detected(const LockPair& mismatch, const LockStac
|
138 | 139 | throw std::logic_error(strprintf("potential deadlock detected: %s -> %s -> %s", mutex_b, mutex_a, mutex_b));
|
139 | 140 | }
|
140 | 141 |
|
| 142 | +static void double_lock_detected(const void* mutex, LockStack& lock_stack) |
| 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 | + |
141 | 159 | template <typename MutexType>
|
142 | 160 | static void push_lock(MutexType* c, const CLockLocation& locklocation)
|
143 | 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 | + |
144 | 166 | LockData& lockdata = GetLockData();
|
145 | 167 | std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
|
146 | 168 |
|
147 | 169 | LockStack& lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
|
148 | 170 | lock_stack.emplace_back(c, locklocation);
|
149 |
| - for (const LockStackItem& i : lock_stack) { |
150 |
| - if (i.first == c) |
151 |
| - 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 | + } |
152 | 186 |
|
153 | 187 | const LockPair p1 = std::make_pair(i.first, c);
|
154 | 188 | if (lockdata.lockorders.count(p1))
|
|
0 commit comments