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