Bitcoin ABC  0.24.7
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_LOCKCONTENTION
23 void PrintLockContention(const char *pszName, const char *pszFile, int nLine) {
24  LogPrintf("LOCKCONTENTION: %s\n", pszName);
25  LogPrintf("Locker: %s:%d\n", pszFile, nLine);
26 }
27 #endif /* DEBUG_LOCKCONTENTION */
28 
29 #ifdef DEBUG_LOCKORDER
30 //
31 // Early deadlock detection.
32 // Problem being solved:
33 // Thread 1 locks A, then B, then C
34 // Thread 2 locks D, then C, then A
35 // --> may result in deadlock between the two threads, depending on when
36 // they run.
37 // Solution implemented here:
38 // Keep track of pairs of locks: (A before B), (A before C), etc.
39 // Complain if any thread tries to lock in a different order.
40 //
41 
42 struct CLockLocation {
43  CLockLocation(const char *pszName, const char *pszFile, int nLine,
44  bool fTryIn, const std::string &thread_name)
45  : fTry(fTryIn), mutexName(pszName), sourceFile(pszFile),
46  m_thread_name(thread_name), sourceLine(nLine) {}
47 
48  std::string ToString() const {
49  return strprintf("'%s' in %s:%s%s (in thread '%s')", mutexName,
50  sourceFile, sourceLine, (fTry ? " (TRY)" : ""),
51  m_thread_name);
52  }
53 
54  std::string Name() const { return mutexName; }
55 
56 private:
57  bool fTry;
58  std::string mutexName;
59  std::string sourceFile;
60  const std::string &m_thread_name;
61  int sourceLine;
62 };
63 
64 using LockStackItem = std::pair<void *, CLockLocation>;
65 using LockStack = std::vector<LockStackItem>;
66 using LockStacks = std::unordered_map<std::thread::id, LockStack>;
67 
68 using LockPair = std::pair<void *, void *>;
69 using LockOrders = std::map<LockPair, LockStack>;
70 using InvLockOrders = std::set<LockPair>;
71 
72 struct LockData {
73  LockStacks m_lock_stacks;
74  LockOrders lockorders;
75  InvLockOrders invlockorders;
76  std::mutex dd_mutex;
77 };
78 
79 LockData &GetLockData() {
80  // This approach guarantees that the object is not destroyed until after its
81  // last use. The operating system automatically reclaims all the memory in a
82  // program's heap when that program exits.
83  // Since the ~LockData() destructor is never called, the LockData class and
84  // all its subclasses must have implicitly-defined destructors.
85  static LockData &lock_data = *new LockData();
86  return lock_data;
87 }
88 
89 static void potential_deadlock_detected(const LockPair &mismatch,
90  const LockStack &s1,
91  const LockStack &s2) {
92  LogPrintf("POTENTIAL DEADLOCK DETECTED\n");
93  LogPrintf("Previous lock order was:\n");
94  for (const LockStackItem &i : s1) {
95  if (i.first == mismatch.first) {
96  LogPrintfToBeContinued(" (1)");
97  }
98  if (i.first == mismatch.second) {
99  LogPrintfToBeContinued(" (2)");
100  }
101  LogPrintf(" %s\n", i.second.ToString());
102  }
103 
104  std::string mutex_a, mutex_b;
105  LogPrintf("Current lock order is:\n");
106  for (const LockStackItem &i : s2) {
107  if (i.first == mismatch.first) {
108  LogPrintfToBeContinued(" (1)");
109  mutex_a = i.second.Name();
110  }
111  if (i.first == mismatch.second) {
112  LogPrintfToBeContinued(" (2)");
113  mutex_b = i.second.Name();
114  }
115  LogPrintf(" %s\n", i.second.ToString());
116  }
117  if (g_debug_lockorder_abort) {
118  tfm::format(
119  std::cerr,
120  "Assertion failed: detected inconsistent lock order for %s, "
121  "details in debug log.\n",
122  s2.back().second.ToString());
123  abort();
124  }
125  throw std::logic_error(
126  strprintf("potential deadlock detected: %s -> %s -> %s", mutex_b,
127  mutex_a, mutex_b));
128 }
129 
130 static void push_lock(void *c, const CLockLocation &locklocation) {
131  LockData &lockdata = GetLockData();
132  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
133 
134  LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
135  lock_stack.emplace_back(c, locklocation);
136  for (const LockStackItem &i : lock_stack) {
137  if (i.first == c) {
138  break;
139  }
140 
141  const LockPair p1 = std::make_pair(i.first, c);
142  if (lockdata.lockorders.count(p1)) {
143  continue;
144  }
145 
146  const LockPair p2 = std::make_pair(c, i.first);
147  if (lockdata.lockorders.count(p2)) {
148  auto lock_stack_copy = lock_stack;
149  lock_stack.pop_back();
150  potential_deadlock_detected(p1, lockdata.lockorders[p2],
151  lock_stack_copy);
152  // potential_deadlock_detected() does not return.
153  }
154 
155  lockdata.lockorders.emplace(p1, lock_stack);
156  lockdata.invlockorders.insert(p2);
157  }
158 }
159 
160 static void pop_lock() {
161  LockData &lockdata = GetLockData();
162  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
163 
164  LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
165  lock_stack.pop_back();
166  if (lock_stack.empty()) {
167  lockdata.m_lock_stacks.erase(std::this_thread::get_id());
168  }
169 }
170 
171 void EnterCritical(const char *pszName, const char *pszFile, int nLine,
172  void *cs, bool fTry) {
173  push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry,
175 }
176 
177 void CheckLastCritical(void *cs, std::string &lockname, const char *guardname,
178  const char *file, int line) {
179  {
180  LockData &lockdata = GetLockData();
181  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
182 
183  const LockStack &lock_stack =
184  lockdata.m_lock_stacks[std::this_thread::get_id()];
185  if (!lock_stack.empty()) {
186  const auto &lastlock = lock_stack.back();
187  if (lastlock.first == cs) {
188  lockname = lastlock.second.Name();
189  return;
190  }
191  }
192  }
193  throw std::system_error(
194  EPERM, std::generic_category(),
195  strprintf("%s:%s %s was not most recent critical section locked", file,
196  line, guardname));
197 }
198 
199 void LeaveCritical() {
200  pop_lock();
201 }
202 
203 std::string LocksHeld() {
204  LockData &lockdata = GetLockData();
205  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
206 
207  const LockStack &lock_stack =
208  lockdata.m_lock_stacks[std::this_thread::get_id()];
209  std::string result;
210  for (const LockStackItem &i : lock_stack) {
211  result += i.second.ToString() + std::string("\n");
212  }
213  return result;
214 }
215 
216 static bool LockHeld(void *mutex) {
217  LockData &lockdata = GetLockData();
218  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
219 
220  const LockStack &lock_stack =
221  lockdata.m_lock_stacks[std::this_thread::get_id()];
222  for (const LockStackItem &i : lock_stack) {
223  if (i.first == mutex) {
224  return true;
225  }
226  }
227 
228  return false;
229 }
230 
231 template <typename MutexType>
232 void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine,
233  MutexType *cs) {
234  if (LockHeld(cs)) {
235  return;
236  }
237  tfm::format(std::cerr,
238  "Assertion failed: lock %s not held in %s:%i; locks held:\n%s",
239  pszName, pszFile, nLine, LocksHeld());
240  abort();
241 }
242 template void AssertLockHeldInternal(const char *, const char *, int, Mutex *);
243 template void AssertLockHeldInternal(const char *, const char *, int,
244  RecursiveMutex *);
245 
246 template <typename MutexType>
247 void AssertLockNotHeldInternal(const char *pszName, const char *pszFile,
248  int nLine, MutexType *cs) {
249  if (!LockHeld(cs)) {
250  return;
251  }
252  tfm::format(std::cerr,
253  "Assertion failed: lock %s held in %s:%i; locks held:\n%s",
254  pszName, pszFile, nLine, LocksHeld());
255  abort();
256 }
257 template void AssertLockNotHeldInternal(const char *, const char *, int,
258  Mutex *);
259 template void AssertLockNotHeldInternal(const char *, const char *, int,
260  RecursiveMutex *);
261 
262 void DeleteLock(void *cs) {
263  LockData &lockdata = GetLockData();
264  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
265  const LockPair item = std::make_pair(cs, nullptr);
266  LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
267  while (it != lockdata.lockorders.end() && it->first.first == cs) {
268  const LockPair invitem =
269  std::make_pair(it->first.second, it->first.first);
270  lockdata.invlockorders.erase(invitem);
271  lockdata.lockorders.erase(it++);
272  }
273  InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item);
274  while (invit != lockdata.invlockorders.end() && invit->first == cs) {
275  const LockPair invinvitem = std::make_pair(invit->second, invit->first);
276  lockdata.lockorders.erase(invinvitem);
277  lockdata.invlockorders.erase(invit++);
278  }
279 }
280 
281 bool LockStackEmpty() {
282  LockData &lockdata = GetLockData();
283  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
284  const auto it = lockdata.m_lock_stacks.find(std::this_thread::get_id());
285  if (it == lockdata.m_lock_stacks.end()) {
286  return true;
287  }
288  return it->second.empty();
289 }
290 
291 bool g_debug_lockorder_abort = true;
292 
293 #endif /* DEBUG_LOCKORDER */
ToString
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:69
tinyformat::format
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:1111
sync.h
LogPrintfToBeContinued
#define LogPrintfToBeContinued
These are aliases used to explicitly state that the message should not end with a newline character.
Definition: logging.h:205
AnnotatedMixin< std::mutex >
CheckLastCritical
void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line)
Definition: sync.h:76
DeleteLock
void DeleteLock(void *cs)
Definition: sync.h:86
LeaveCritical
void LeaveCritical()
Definition: sync.h:75
cs
static void pool cs
Definition: mempool_eviction.cpp:11
tinyformat.h
strencodings.h
EnterCritical
void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry=false)
Definition: sync.h:73
AssertLockHeldInternal
void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: sync.h:80
util::ThreadGetInternalName
const std::string & ThreadGetInternalName()
Get the thread's internal (in-memory) name; used e.g.
Definition: threadnames.cpp:39
LockStackEmpty
bool LockStackEmpty()
Definition: sync.h:87
strprintf
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1201
logging.h
threadnames.h
AssertLockNotHeldInternal
void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) LOCKS_EXCLUDED(cs)
Definition: sync.h:84
LogPrintf
static void LogPrintf(const char *fmt, const Args &... args)
Definition: logging.h:175