Bitcoin Core  0.19.99
P2P Digital Currency
sync.cpp
Go to the documentation of this file.
1 // Copyright (c) 2011-2020 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 #if defined(HAVE_CONFIG_H)
7 #endif
8 
9 #include <sync.h>
10 #include <tinyformat.h>
11 
12 #include <logging.h>
13 #include <util/strencodings.h>
14 #include <util/threadnames.h>
15 
16 
17 #include <map>
18 #include <set>
19 
20 #ifdef DEBUG_LOCKCONTENTION
21 #if !defined(HAVE_THREAD_LOCAL)
22 static_assert(false, "thread_local is not supported");
23 #endif
24 void PrintLockContention(const char* pszName, const char* pszFile, int nLine)
25 {
26  LogPrintf("LOCKCONTENTION: %s\n", pszName);
27  LogPrintf("Locker: %s:%d\n", pszFile, nLine);
28 }
29 #endif /* DEBUG_LOCKCONTENTION */
30 
31 #ifdef DEBUG_LOCKORDER
32 //
33 // Early deadlock detection.
34 // Problem being solved:
35 // Thread 1 locks A, then B, then C
36 // Thread 2 locks D, then C, then A
37 // --> may result in deadlock between the two threads, depending on when they run.
38 // Solution implemented here:
39 // Keep track of pairs of locks: (A before B), (A before C), etc.
40 // Complain if any thread tries to lock in a different order.
41 //
42 
43 struct CLockLocation {
44  CLockLocation(
45  const char* pszName,
46  const char* pszFile,
47  int nLine,
48  bool fTryIn,
49  const std::string& thread_name)
50  : fTry(fTryIn),
51  mutexName(pszName),
52  sourceFile(pszFile),
53  m_thread_name(thread_name),
54  sourceLine(nLine) {}
55 
56  std::string ToString() const
57  {
58  return strprintf(
59  "%s %s:%s%s (in thread %s)",
60  mutexName, sourceFile, itostr(sourceLine), (fTry ? " (TRY)" : ""), m_thread_name);
61  }
62 
63 private:
64  bool fTry;
65  std::string mutexName;
66  std::string sourceFile;
67  const std::string& m_thread_name;
68  int sourceLine;
69 };
70 
71 typedef std::vector<std::pair<void*, CLockLocation> > LockStack;
72 typedef std::map<std::pair<void*, void*>, LockStack> LockOrders;
73 typedef std::set<std::pair<void*, void*> > InvLockOrders;
74 
75 struct LockData {
76  // Very ugly hack: as the global constructs and destructors run single
77  // threaded, we use this boolean to know whether LockData still exists,
78  // as DeleteLock can get called by global RecursiveMutex destructors
79  // after LockData disappears.
80  bool available;
81  LockData() : available(true) {}
82  ~LockData() { available = false; }
83 
84  LockOrders lockorders;
85  InvLockOrders invlockorders;
86  std::mutex dd_mutex;
87 };
88 LockData& GetLockData() {
89  static LockData lockdata;
90  return lockdata;
91 }
92 
93 static thread_local LockStack g_lockstack;
94 
95 static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2)
96 {
97  LogPrintf("POTENTIAL DEADLOCK DETECTED\n");
98  LogPrintf("Previous lock order was:\n");
99  for (const std::pair<void*, CLockLocation> & i : s2) {
100  if (i.first == mismatch.first) {
101  LogPrintf(" (1)"); /* Continued */
102  }
103  if (i.first == mismatch.second) {
104  LogPrintf(" (2)"); /* Continued */
105  }
106  LogPrintf(" %s\n", i.second.ToString());
107  }
108  LogPrintf("Current lock order is:\n");
109  for (const std::pair<void*, CLockLocation> & i : s1) {
110  if (i.first == mismatch.first) {
111  LogPrintf(" (1)"); /* Continued */
112  }
113  if (i.first == mismatch.second) {
114  LogPrintf(" (2)"); /* Continued */
115  }
116  LogPrintf(" %s\n", i.second.ToString());
117  }
118  if (g_debug_lockorder_abort) {
119  tfm::format(std::cerr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__);
120  abort();
121  }
122  throw std::logic_error("potential deadlock detected");
123 }
124 
125 static void push_lock(void* c, const CLockLocation& locklocation)
126 {
127  LockData& lockdata = GetLockData();
128  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
129 
130  g_lockstack.push_back(std::make_pair(c, locklocation));
131 
132  for (const std::pair<void*, CLockLocation>& i : g_lockstack) {
133  if (i.first == c)
134  break;
135 
136  std::pair<void*, void*> p1 = std::make_pair(i.first, c);
137  if (lockdata.lockorders.count(p1))
138  continue;
139  lockdata.lockorders.emplace(p1, g_lockstack);
140 
141  std::pair<void*, void*> p2 = std::make_pair(c, i.first);
142  lockdata.invlockorders.insert(p2);
143  if (lockdata.lockorders.count(p2))
144  potential_deadlock_detected(p1, lockdata.lockorders[p2], lockdata.lockorders[p1]);
145  }
146 }
147 
148 static void pop_lock()
149 {
150  g_lockstack.pop_back();
151 }
152 
153 void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
154 {
155  push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry, util::ThreadGetInternalName()));
156 }
157 
158 void LeaveCritical()
159 {
160  pop_lock();
161 }
162 
163 std::string LocksHeld()
164 {
165  std::string result;
166  for (const std::pair<void*, CLockLocation>& i : g_lockstack)
167  result += i.second.ToString() + std::string("\n");
168  return result;
169 }
170 
171 void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
172 {
173  for (const std::pair<void*, CLockLocation>& i : g_lockstack)
174  if (i.first == cs)
175  return;
176  tfm::format(std::cerr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
177  abort();
178 }
179 
180 void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
181 {
182  for (const std::pair<void*, CLockLocation>& i : g_lockstack) {
183  if (i.first == cs) {
184  tfm::format(std::cerr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld());
185  abort();
186  }
187  }
188 }
189 
190 void DeleteLock(void* cs)
191 {
192  LockData& lockdata = GetLockData();
193  if (!lockdata.available) {
194  // We're already shutting down.
195  return;
196  }
197  std::lock_guard<std::mutex> lock(lockdata.dd_mutex);
198  std::pair<void*, void*> item = std::make_pair(cs, nullptr);
199  LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
200  while (it != lockdata.lockorders.end() && it->first.first == cs) {
201  std::pair<void*, void*> invitem = std::make_pair(it->first.second, it->first.first);
202  lockdata.invlockorders.erase(invitem);
203  lockdata.lockorders.erase(it++);
204  }
205  InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item);
206  while (invit != lockdata.invlockorders.end() && invit->first == cs) {
207  std::pair<void*, void*> invinvitem = std::make_pair(invit->second, invit->first);
208  lockdata.lockorders.erase(invinvitem);
209  lockdata.invlockorders.erase(invit++);
210  }
211 }
212 
213 bool g_debug_lockorder_abort = true;
214 
215 #endif /* DEBUG_LOCKORDER */
static void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, void *cs) ASSERT_EXCLUSIVE_LOCK(cs)
Definition: sync.h:67
static void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, void *cs)
Definition: sync.h:68
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164
static void pool cs
static void LogPrintf(const char *fmt, const Args &... args)
Definition: logging.h:163
std::string itostr(int n)
static void DeleteLock(void *cs)
Definition: sync.h:69
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:1062
static void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry=false)
Definition: sync.h:65
static void LeaveCritical()
Definition: sync.h:66
const std::string & ThreadGetInternalName()
Get the thread&#39;s internal (in-memory) name; used e.g.
Definition: threadnames.cpp:53
auto it
Definition: validation.cpp:362