/ src / test / sync_tests.cpp
sync_tests.cpp
  1  // Copyright (c) 2012-present 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  #include <test/util/common.h>
  7  
  8  #include <boost/test/unit_test.hpp>
  9  
 10  #include <mutex>
 11  #include <stdexcept>
 12  
 13  namespace {
 14  template <typename MutexType>
 15  void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2)
 16  {
 17      {
 18          LOCK2(mutex1, mutex2);
 19      }
 20      BOOST_CHECK(LockStackEmpty());
 21      bool error_thrown = false;
 22      try {
 23          LOCK2(mutex2, mutex1);
 24      } catch (const std::logic_error& e) {
 25          BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected: mutex1 -> mutex2 -> mutex1");
 26          error_thrown = true;
 27      }
 28      BOOST_CHECK(LockStackEmpty());
 29      #ifdef DEBUG_LOCKORDER
 30      BOOST_CHECK(error_thrown);
 31      #else
 32      BOOST_CHECK(!error_thrown);
 33      #endif
 34  }
 35  
 36  #ifdef DEBUG_LOCKORDER
 37  template <typename MutexType>
 38  void TestDoubleLock2(MutexType& m)
 39  {
 40      LOCK(m);
 41  }
 42  
 43  template <typename MutexType>
 44  void TestDoubleLock(bool should_throw)
 45  {
 46      const bool prev = g_debug_lockorder_abort;
 47      g_debug_lockorder_abort = false;
 48  
 49      MutexType m;
 50      {
 51          LOCK(m);
 52          if (should_throw) {
 53              BOOST_CHECK_EXCEPTION(TestDoubleLock2(m), std::logic_error,
 54                                HasReason("double lock detected"));
 55          } else {
 56              BOOST_CHECK_NO_THROW(TestDoubleLock2(m));
 57          }
 58      }
 59      BOOST_CHECK(LockStackEmpty());
 60  
 61      g_debug_lockorder_abort = prev;
 62  }
 63  #endif /* DEBUG_LOCKORDER */
 64  
 65  template <typename MutexType>
 66  void TestInconsistentLockOrderDetected(MutexType& mutex1, MutexType& mutex2)
 67  {
 68      {
 69          WAIT_LOCK(mutex1, lock1);
 70          LOCK(mutex2);
 71  #ifdef DEBUG_LOCKORDER
 72          BOOST_CHECK_EXCEPTION(REVERSE_LOCK(lock1, mutex1), std::logic_error, HasReason("mutex1 was not most recent critical section locked"));
 73  #endif // DEBUG_LOCKORDER
 74      }
 75      BOOST_CHECK(LockStackEmpty());
 76  }
 77  } // namespace
 78  
 79  BOOST_AUTO_TEST_SUITE(sync_tests)
 80  
 81  BOOST_AUTO_TEST_CASE(potential_deadlock_detected)
 82  {
 83      #ifdef DEBUG_LOCKORDER
 84      bool prev = g_debug_lockorder_abort;
 85      g_debug_lockorder_abort = false;
 86      #endif
 87  
 88      RecursiveMutex rmutex1, rmutex2;
 89      TestPotentialDeadLockDetected(rmutex1, rmutex2);
 90      // The second test ensures that lock tracking data have not been broken by exception.
 91      TestPotentialDeadLockDetected(rmutex1, rmutex2);
 92  
 93      Mutex mutex1, mutex2;
 94      TestPotentialDeadLockDetected(mutex1, mutex2);
 95      // The second test ensures that lock tracking data have not been broken by exception.
 96      TestPotentialDeadLockDetected(mutex1, mutex2);
 97  
 98      #ifdef DEBUG_LOCKORDER
 99      g_debug_lockorder_abort = prev;
100      #endif
101  }
102  
103  /* Double lock would produce an undefined behavior. Thus, we only do that if
104   * DEBUG_LOCKORDER is activated to detect it. We don't want non-DEBUG_LOCKORDER
105   * build to produce tests that exhibit known undefined behavior. */
106  #ifdef DEBUG_LOCKORDER
107  BOOST_AUTO_TEST_CASE(double_lock_mutex)
108  {
109      TestDoubleLock<Mutex>(/*should_throw=*/true);
110  }
111  
112  BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex)
113  {
114      TestDoubleLock<RecursiveMutex>(/*should_throw=*/false);
115  }
116  #endif /* DEBUG_LOCKORDER */
117  
118  BOOST_AUTO_TEST_CASE(inconsistent_lock_order_detected)
119  {
120  #ifdef DEBUG_LOCKORDER
121      bool prev = g_debug_lockorder_abort;
122      g_debug_lockorder_abort = false;
123  #endif // DEBUG_LOCKORDER
124  
125      RecursiveMutex rmutex1, rmutex2;
126      TestInconsistentLockOrderDetected(rmutex1, rmutex2);
127      // By checking lock order consistency (CheckLastCritical) before any unlocking (LeaveCritical)
128      // the lock tracking data must not have been broken by exception.
129      TestInconsistentLockOrderDetected(rmutex1, rmutex2);
130  
131      Mutex mutex1, mutex2;
132      TestInconsistentLockOrderDetected(mutex1, mutex2);
133      // By checking lock order consistency (CheckLastCritical) before any unlocking (LeaveCritical)
134      // the lock tracking data must not have been broken by exception.
135      TestInconsistentLockOrderDetected(mutex1, mutex2);
136  
137  #ifdef DEBUG_LOCKORDER
138      g_debug_lockorder_abort = prev;
139  #endif // DEBUG_LOCKORDER
140  }
141  
142  BOOST_AUTO_TEST_SUITE_END()