Bitcoin ABC  0.26.3
P2P Digital Currency
sync.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2016 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <sync.h>
6 
7 #include <logging.h>
8 #include <tinyformat.h>
9 #include <util/strencodings.h>
10 #include <util/threadnames.h>
11 
12 #include <tinyformat.h>
13 
14 #include <map>
15 #include <set>
16 #include <system_error>
17 #include <thread>
18 #include <unordered_map>
19 #include <utility>
20 #include <vector>
21 
22 #ifdef DEBUG_LOCKORDER
23 //
24 // Early deadlock detection.
25 // Problem being solved:
26 // Thread 1 locks A, then B, then C
27 // Thread 2 locks D, then C, then A
28 // --> may result in deadlock between the two threads, depending on when
29 // they run.
30 // Solution implemented here:
31 // Keep track of pairs of locks: (A before B), (A before C), etc.
32 // Complain if any thread tries to lock in a different order.
33 //
34 
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) {}
40 
41  std::string ToString() const {
42  return strprintf("'%s' in %s:%s%s (in thread '%s')", mutexName,
43  sourceFile, sourceLine, (fTry ? " (TRY)" : ""),
44  m_thread_name);
45  }
46 
47  std::string Name() const { return mutexName; }
48 
49 private:
50  bool fTry;
51  std::string mutexName;
52  std::string sourceFile;
53  const std::string &m_thread_name;
54  int sourceLine;
55 };
56 
57 using LockStackItem = std::pair<void *, CLockLocation>;
58 using LockStack = std::vector<LockStackItem>;
59 using LockStacks = std::unordered_map<std::thread::id, LockStack>;
60 
61 using LockPair = std::pair<void *, void *>;
62 using LockOrders = std::map<LockPair, LockStack>;
63 using InvLockOrders = std::set<LockPair>;
64 
65 struct LockData {
66  LockStacks m_lock_stacks;
67  LockOrders lockorders;
68  InvLockOrders invlockorders;
69  std::mutex dd_mutex;
70 };
71 
72 LockData &GetLockData() {
73  // This approach guarantees that the object is not destroyed until after its
74  // last use. The operating system automatically reclaims all the memory in a
75  // program's heap when that program exits.
76  // Since the ~LockData() destructor is never called, the LockData class and
77  // all its subclasses must have implicitly-defined destructors.
78  static LockData &lock_data = *new LockData();
79  return lock_data;
80 }
81 
82 static void potential_deadlock_detected(const LockPair &mismatch,
83  const LockStack &s1,
84  const LockStack &s2) {
85  LogPrintf("POTENTIAL DEADLOCK DETECTED\n");
86  LogPrintf("Previous lock order was:\n");
87  for (const LockStackItem &i : s1) {
88  if (i.first == mismatch.first) {
89  LogPrintfToBeContinued(" (1)");
90  }
91  if (i.first == mismatch.second) {
92  LogPrintfToBeContinued(" (2)");
93  }
94  LogPrintf(" %s\n", i.second.ToString());
95  }
96 
97  std::string mutex_a, mutex_b;
98  LogPrintf("Current lock order is:\n");
99  for (const LockStackItem &i : s2) {
100  if (i.first == mismatch.first) {
101  LogPrintfToBeContinued(" (1)");
102  mutex_a = i.second.Name();
103  }
104  if (i.first == mismatch.second) {
105  LogPrintfToBeContinued(" (2)");
106  mutex_b = i.second.Name();
107  }
108  LogPrintf(" %s\n", i.second.ToString());
109  }
110  if (g_debug_lockorder_abort) {
111  tfm::format(
112  std::cerr,
113  "Assertion failed: detected inconsistent lock order for %s, "
114  "details in debug log.\n",
115  s2.back().second.ToString());
116  abort();
117  }
118  throw std::logic_error(
119  strprintf("potential deadlock detected: %s -> %s -> %s", mutex_b,
120  mutex_a, mutex_b));
121 }
122 
123 static void push_lock(void *c, const CLockLocation &locklocation) {
124  LockData &lockdata = GetLockData();
125  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
126 
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) {
130  if (i.first == c) {
131  break;
132  }
133 
134  const LockPair p1 = std::make_pair(i.first, c);
135  if (lockdata.lockorders.count(p1)) {
136  continue;
137  }
138 
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],
144  lock_stack_copy);
145  // potential_deadlock_detected() does not return.
146  }
147 
148  lockdata.lockorders.emplace(p1, lock_stack);
149  lockdata.invlockorders.insert(p2);
150  }
151 }
152 
153 static void pop_lock() {
154  LockData &lockdata = GetLockData();
155  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
156 
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());
161  }
162 }
163 
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,
168 }
169 
170 void CheckLastCritical(void *cs, std::string &lockname, const char *guardname,
171  const char *file, int line) {
172  LockData &lockdata = GetLockData();
173  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
174 
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();
181  return;
182  }
183  }
184 
185  LogPrintf("INCONSISTENT LOCK ORDER DETECTED\n");
186  LogPrintf("Current lock order (least recent first) is:\n");
187  for (const LockStackItem &i : lock_stack) {
188  LogPrintf(" %s\n", i.second.ToString());
189  }
190  if (g_debug_lockorder_abort) {
191  tfm::format(std::cerr,
192  "%s:%s %s was not most recent critical section locked, "
193  "details in debug log.\n",
194  file, line, guardname);
195  abort();
196  }
197  throw std::logic_error(
198  strprintf("%s was not most recent critical section locked", guardname));
199 }
200 
201 void LeaveCritical() {
202  pop_lock();
203 }
204 
205 std::string LocksHeld() {
206  LockData &lockdata = GetLockData();
207  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
208 
209  const LockStack &lock_stack =
210  lockdata.m_lock_stacks[std::this_thread::get_id()];
211  std::string result;
212  for (const LockStackItem &i : lock_stack) {
213  result += i.second.ToString() + std::string("\n");
214  }
215  return result;
216 }
217 
218 static bool LockHeld(void *mutex) {
219  LockData &lockdata = GetLockData();
220  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
221 
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) {
226  return true;
227  }
228  }
229 
230  return false;
231 }
232 
233 template <typename MutexType>
234 void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine,
235  MutexType *cs) {
236  if (LockHeld(cs)) {
237  return;
238  }
239  tfm::format(std::cerr,
240  "Assertion failed: lock %s not held in %s:%i; locks held:\n%s",
241  pszName, pszFile, nLine, LocksHeld());
242  abort();
243 }
244 template void AssertLockHeldInternal(const char *, const char *, int, Mutex *);
245 template void AssertLockHeldInternal(const char *, const char *, int,
246  RecursiveMutex *);
247 
248 template <typename MutexType>
249 void AssertLockNotHeldInternal(const char *pszName, const char *pszFile,
250  int nLine, MutexType *cs) {
251  if (!LockHeld(cs)) {
252  return;
253  }
254  tfm::format(std::cerr,
255  "Assertion failed: lock %s held in %s:%i; locks held:\n%s",
256  pszName, pszFile, nLine, LocksHeld());
257  abort();
258 }
259 template void AssertLockNotHeldInternal(const char *, const char *, int,
260  Mutex *);
261 template void AssertLockNotHeldInternal(const char *, const char *, int,
262  RecursiveMutex *);
263 
264 void DeleteLock(void *cs) {
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++);
274  }
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++);
280  }
281 }
282 
283 bool LockStackEmpty() {
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()) {
288  return true;
289  }
290  return it->second.empty();
291 }
292 
293 bool g_debug_lockorder_abort = true;
294 
295 #endif /* DEBUG_LOCKORDER */
#define LogPrintfToBeContinued
These are aliases used to explicitly state that the message should not end with a newline character.
Definition: logging.h:221
#define LogPrintf(...)
Definition: logging.h:205
static void pool cs
void format(std::ostream &out, const char *fmt, const Args &...args)
Format list of arguments to the stream according to given format string.
Definition: tinyformat.h:1112
const std::string & ThreadGetInternalName()
Get the thread's internal (in-memory) name; used e.g.
Definition: threadnames.cpp:39
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:79
void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: sync.h:85
void DeleteLock(void *cs)
Definition: sync.h:91
void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line)
Definition: sync.h:81
void LeaveCritical()
Definition: sync.h:80
void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry=false)
Definition: sync.h:78
bool LockStackEmpty()
Definition: sync.h:92
void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) LOCKS_EXCLUDED(cs)
Definition: sync.h:89
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202