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