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