Bitcoin ABC  0.22.12
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 %s:%s%s (in thread %s)", mutexName, sourceFile,
50  sourceLine, (fTry ? " (TRY)" : ""), m_thread_name);
51  }
52 
53  std::string Name() const { return mutexName; }
54 
55 private:
56  bool fTry;
57  std::string mutexName;
58  std::string sourceFile;
59  const std::string &m_thread_name;
60  int sourceLine;
61 };
62 
63 using LockStackItem = std::pair<void *, CLockLocation>;
64 using LockStack = std::vector<LockStackItem>;
65 using LockStacks = std::unordered_map<std::thread::id, LockStack>;
66 
67 using LockPair = std::pair<void *, void *>;
68 using LockOrders = std::map<LockPair, LockStack>;
69 using InvLockOrders = std::set<LockPair>;
70 
71 struct LockData {
72  LockStacks m_lock_stacks;
73  LockOrders lockorders;
74  InvLockOrders invlockorders;
75  std::mutex dd_mutex;
76 };
77 
78 LockData &GetLockData() {
79  // This approach guarantees that the object is not destroyed until after its
80  // last use. The operating system automatically reclaims all the memory in a
81  // program's heap when that program exits.
82  // Since the ~LockData() destructor is never called, the LockData class and
83  // all its subclasses must have implicitly-defined destructors.
84  static LockData &lock_data = *new LockData();
85  return lock_data;
86 }
87 
88 static void potential_deadlock_detected(const LockPair &mismatch,
89  const LockStack &s1,
90  const LockStack &s2) {
91  LogPrintf("POTENTIAL DEADLOCK DETECTED\n");
92  LogPrintf("Previous lock order was:\n");
93  for (const LockStackItem &i : s2) {
94  if (i.first == mismatch.first) {
95  LogPrintfToBeContinued(" (1)");
96  }
97  if (i.first == mismatch.second) {
98  LogPrintfToBeContinued(" (2)");
99  }
100  LogPrintf(" %s\n", i.second.ToString());
101  }
102  LogPrintf("Current lock order is:\n");
103  for (const LockStackItem &i : s1) {
104  if (i.first == mismatch.first) {
105  LogPrintfToBeContinued(" (1)");
106  }
107  if (i.first == mismatch.second) {
108  LogPrintfToBeContinued(" (2)");
109  }
110  LogPrintf(" %s\n", i.second.ToString());
111  }
112  if (g_debug_lockorder_abort) {
113  tfm::format(
114  std::cerr,
115  "Assertion failed: detected inconsistent lock order at %s:%i, "
116  "details in debug log.\n",
117  __FILE__, __LINE__);
118  abort();
119  }
120  throw std::logic_error("potential deadlock detected");
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  lockdata.lockorders.emplace(p1, lock_stack);
139 
140  const LockPair p2 = std::make_pair(c, i.first);
141  lockdata.invlockorders.insert(p2);
142  if (lockdata.lockorders.count(p2)) {
143  potential_deadlock_detected(p1, lockdata.lockorders[p2],
144  lockdata.lockorders[p1]);
145  }
146  }
147 }
148 
149 static void pop_lock() {
150  LockData &lockdata = GetLockData();
151  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
152 
153  LockStack &lock_stack = lockdata.m_lock_stacks[std::this_thread::get_id()];
154  lock_stack.pop_back();
155  if (lock_stack.empty()) {
156  lockdata.m_lock_stacks.erase(std::this_thread::get_id());
157  }
158 }
159 
160 void EnterCritical(const char *pszName, const char *pszFile, int nLine,
161  void *cs, bool fTry) {
162  push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry,
164 }
165 
166 void CheckLastCritical(void *cs, std::string &lockname, const char *guardname,
167  const char *file, int line) {
168  {
169  LockData &lockdata = GetLockData();
170  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
171 
172  const LockStack &lock_stack =
173  lockdata.m_lock_stacks[std::this_thread::get_id()];
174  if (!lock_stack.empty()) {
175  const auto &lastlock = lock_stack.back();
176  if (lastlock.first == cs) {
177  lockname = lastlock.second.Name();
178  return;
179  }
180  }
181  }
182  throw std::system_error(
183  EPERM, std::generic_category(),
184  strprintf("%s:%s %s was not most recent critical section locked", file,
185  line, guardname));
186 }
187 
188 void LeaveCritical() {
189  pop_lock();
190 }
191 
192 std::string LocksHeld() {
193  LockData &lockdata = GetLockData();
194  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
195 
196  const LockStack &lock_stack =
197  lockdata.m_lock_stacks[std::this_thread::get_id()];
198  std::string result;
199  for (const LockStackItem &i : lock_stack) {
200  result += i.second.ToString() + std::string("\n");
201  }
202  return result;
203 }
204 
205 static bool LockHeld(void *mutex) {
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  for (const LockStackItem &i : lock_stack) {
212  if (i.first == mutex) {
213  return true;
214  }
215  }
216 
217  return false;
218 }
219 
220 template <typename MutexType>
221 void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine,
222  MutexType *cs) {
223  if (LockHeld(cs)) {
224  return;
225  }
226  tfm::format(std::cerr,
227  "Assertion failed: lock %s not held in %s:%i; locks held:\n%s",
228  pszName, pszFile, nLine, LocksHeld());
229  abort();
230 }
231 template void AssertLockHeldInternal(const char *, const char *, int, Mutex *);
232 template void AssertLockHeldInternal(const char *, const char *, int,
233  RecursiveMutex *);
234 
235 void AssertLockNotHeldInternal(const char *pszName, const char *pszFile,
236  int nLine, void *cs) {
237  if (!LockHeld(cs)) {
238  return;
239  }
240  tfm::format(std::cerr,
241  "Assertion failed: lock %s held in %s:%i; locks held:\n%s",
242  pszName, pszFile, nLine, LocksHeld());
243  abort();
244 }
245 
246 void DeleteLock(void *cs) {
247  LockData &lockdata = GetLockData();
248  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
249  const LockPair item = std::make_pair(cs, nullptr);
250  LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
251  while (it != lockdata.lockorders.end() && it->first.first == cs) {
252  const LockPair invitem =
253  std::make_pair(it->first.second, it->first.first);
254  lockdata.invlockorders.erase(invitem);
255  lockdata.lockorders.erase(it++);
256  }
257  InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item);
258  while (invit != lockdata.invlockorders.end() && invit->first == cs) {
259  const LockPair invinvitem = std::make_pair(invit->second, invit->first);
260  lockdata.lockorders.erase(invinvitem);
261  lockdata.invlockorders.erase(invit++);
262  }
263 }
264 
265 bool g_debug_lockorder_abort = true;
266 
267 #endif /* DEBUG_LOCKORDER */
static void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, void *cs)
Definition: sync.h:82
#define LogPrintfToBeContinued
These are aliases used to explicitly state that the message should not end with a newline character...
Definition: logging.h:201
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1201
static void pool cs
static void LogPrintf(const char *fmt, const Args &... args)
Definition: logging.h:171
static void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) ASSERT_EXCLUSIVE_LOCK(cs)
Definition: sync.h:78
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:67
static void DeleteLock(void *cs)
Definition: sync.h:85
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
static void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line)
Definition: sync.h:74
static void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry=false)
Definition: sync.h:71
static void LeaveCritical()
Definition: sync.h:73
const std::string & ThreadGetInternalName()
Get the thread&#39;s internal (in-memory) name; used e.g.
Definition: threadnames.cpp:39