sync_tests.cpp raw

   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()
 143