You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

sync.cpp 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Copyright (c) 2011-2016 The Starwels developers
  2. // Distributed under the MIT software license, see the accompanying
  3. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  4. #include "sync.h"
  5. #include "util.h"
  6. #include "utilstrencodings.h"
  7. #include <stdio.h>
  8. #include <boost/thread.hpp>
  9. #ifdef DEBUG_LOCKCONTENTION
  10. void PrintLockContention(const char* pszName, const char* pszFile, int nLine)
  11. {
  12. LogPrintf("LOCKCONTENTION: %s\n", pszName);
  13. LogPrintf("Locker: %s:%d\n", pszFile, nLine);
  14. }
  15. #endif /* DEBUG_LOCKCONTENTION */
  16. #ifdef DEBUG_LOCKORDER
  17. //
  18. // Early deadlock detection.
  19. // Problem being solved:
  20. // Thread 1 locks A, then B, then C
  21. // Thread 2 locks D, then C, then A
  22. // --> may result in deadlock between the two threads, depending on when they run.
  23. // Solution implemented here:
  24. // Keep track of pairs of locks: (A before B), (A before C), etc.
  25. // Complain if any thread tries to lock in a different order.
  26. //
  27. struct CLockLocation {
  28. CLockLocation(const char* pszName, const char* pszFile, int nLine, bool fTryIn)
  29. {
  30. mutexName = pszName;
  31. sourceFile = pszFile;
  32. sourceLine = nLine;
  33. fTry = fTryIn;
  34. }
  35. std::string ToString() const
  36. {
  37. return mutexName + " " + sourceFile + ":" + itostr(sourceLine) + (fTry ? " (TRY)" : "");
  38. }
  39. bool fTry;
  40. private:
  41. std::string mutexName;
  42. std::string sourceFile;
  43. int sourceLine;
  44. };
  45. typedef std::vector<std::pair<void*, CLockLocation> > LockStack;
  46. typedef std::map<std::pair<void*, void*>, LockStack> LockOrders;
  47. typedef std::set<std::pair<void*, void*> > InvLockOrders;
  48. struct LockData {
  49. // Very ugly hack: as the global constructs and destructors run single
  50. // threaded, we use this boolean to know whether LockData still exists,
  51. // as DeleteLock can get called by global CCriticalSection destructors
  52. // after LockData disappears.
  53. bool available;
  54. LockData() : available(true) {}
  55. ~LockData() { available = false; }
  56. LockOrders lockorders;
  57. InvLockOrders invlockorders;
  58. boost::mutex dd_mutex;
  59. } static lockdata;
  60. boost::thread_specific_ptr<LockStack> lockstack;
  61. static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2)
  62. {
  63. LogPrintf("POTENTIAL DEADLOCK DETECTED\n");
  64. LogPrintf("Previous lock order was:\n");
  65. for (const std::pair<void*, CLockLocation> & i : s2) {
  66. if (i.first == mismatch.first) {
  67. LogPrintf(" (1)");
  68. }
  69. if (i.first == mismatch.second) {
  70. LogPrintf(" (2)");
  71. }
  72. LogPrintf(" %s\n", i.second.ToString());
  73. }
  74. LogPrintf("Current lock order is:\n");
  75. for (const std::pair<void*, CLockLocation> & i : s1) {
  76. if (i.first == mismatch.first) {
  77. LogPrintf(" (1)");
  78. }
  79. if (i.first == mismatch.second) {
  80. LogPrintf(" (2)");
  81. }
  82. LogPrintf(" %s\n", i.second.ToString());
  83. }
  84. assert(false);
  85. }
  86. static void push_lock(void* c, const CLockLocation& locklocation, bool fTry)
  87. {
  88. if (lockstack.get() == nullptr)
  89. lockstack.reset(new LockStack);
  90. boost::unique_lock<boost::mutex> lock(lockdata.dd_mutex);
  91. (*lockstack).push_back(std::make_pair(c, locklocation));
  92. for (const std::pair<void*, CLockLocation> & i : (*lockstack)) {
  93. if (i.first == c)
  94. break;
  95. std::pair<void*, void*> p1 = std::make_pair(i.first, c);
  96. if (lockdata.lockorders.count(p1))
  97. continue;
  98. lockdata.lockorders[p1] = (*lockstack);
  99. std::pair<void*, void*> p2 = std::make_pair(c, i.first);
  100. lockdata.invlockorders.insert(p2);
  101. if (lockdata.lockorders.count(p2))
  102. potential_deadlock_detected(p1, lockdata.lockorders[p2], lockdata.lockorders[p1]);
  103. }
  104. }
  105. static void pop_lock()
  106. {
  107. (*lockstack).pop_back();
  108. }
  109. void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry)
  110. {
  111. push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry), fTry);
  112. }
  113. void LeaveCritical()
  114. {
  115. pop_lock();
  116. }
  117. std::string LocksHeld()
  118. {
  119. std::string result;
  120. for (const std::pair<void*, CLockLocation> & i : *lockstack)
  121. result += i.second.ToString() + std::string("\n");
  122. return result;
  123. }
  124. void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs)
  125. {
  126. for (const std::pair<void*, CLockLocation> & i : *lockstack)
  127. if (i.first == cs)
  128. return;
  129. fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str());
  130. abort();
  131. }
  132. void DeleteLock(void* cs)
  133. {
  134. if (!lockdata.available) {
  135. // We're already shutting down.
  136. return;
  137. }
  138. boost::unique_lock<boost::mutex> lock(lockdata.dd_mutex);
  139. std::pair<void*, void*> item = std::make_pair(cs, (void*)0);
  140. LockOrders::iterator it = lockdata.lockorders.lower_bound(item);
  141. while (it != lockdata.lockorders.end() && it->first.first == cs) {
  142. std::pair<void*, void*> invitem = std::make_pair(it->first.second, it->first.first);
  143. lockdata.invlockorders.erase(invitem);
  144. lockdata.lockorders.erase(it++);
  145. }
  146. InvLockOrders::iterator invit = lockdata.invlockorders.lower_bound(item);
  147. while (invit != lockdata.invlockorders.end() && invit->first == cs) {
  148. std::pair<void*, void*> invinvitem = std::make_pair(invit->second, invit->first);
  149. lockdata.lockorders.erase(invinvitem);
  150. lockdata.invlockorders.erase(invit++);
  151. }
  152. }
  153. #endif /* DEBUG_LOCKORDER */