sync_tests.cpp
1 // Copyright (c) 2012-2022 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/setup_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 ENTER_CRITICAL_SECTION(m); 41 LEAVE_CRITICAL_SECTION(m); 42 } 43 44 template <typename MutexType> 45 void TestDoubleLock(bool should_throw) 46 { 47 const bool prev = g_debug_lockorder_abort; 48 g_debug_lockorder_abort = false; 49 50 MutexType m; 51 ENTER_CRITICAL_SECTION(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 LEAVE_CRITICAL_SECTION(m); 59 60 BOOST_CHECK(LockStackEmpty()); 61 62 g_debug_lockorder_abort = prev; 63 } 64 #endif /* DEBUG_LOCKORDER */ 65 66 template <typename MutexType> 67 void TestInconsistentLockOrderDetected(MutexType& mutex1, MutexType& mutex2) NO_THREAD_SAFETY_ANALYSIS 68 { 69 ENTER_CRITICAL_SECTION(mutex1); 70 ENTER_CRITICAL_SECTION(mutex2); 71 #ifdef DEBUG_LOCKORDER 72 BOOST_CHECK_EXCEPTION(LEAVE_CRITICAL_SECTION(mutex1), std::logic_error, HasReason("mutex1 was not most recent critical section locked")); 73 #endif // DEBUG_LOCKORDER 74 LEAVE_CRITICAL_SECTION(mutex2); 75 LEAVE_CRITICAL_SECTION(mutex1); 76 BOOST_CHECK(LockStackEmpty()); 77 } 78 } // namespace 79 80 BOOST_AUTO_TEST_SUITE(sync_tests) 81 82 BOOST_AUTO_TEST_CASE(potential_deadlock_detected) 83 { 84 #ifdef DEBUG_LOCKORDER 85 bool prev = g_debug_lockorder_abort; 86 g_debug_lockorder_abort = false; 87 #endif 88 89 RecursiveMutex rmutex1, rmutex2; 90 TestPotentialDeadLockDetected(rmutex1, rmutex2); 91 // The second test ensures that lock tracking data have not been broken by exception. 92 TestPotentialDeadLockDetected(rmutex1, rmutex2); 93 94 Mutex mutex1, mutex2; 95 TestPotentialDeadLockDetected(mutex1, mutex2); 96 // The second test ensures that lock tracking data have not been broken by exception. 97 TestPotentialDeadLockDetected(mutex1, mutex2); 98 99 #ifdef DEBUG_LOCKORDER 100 g_debug_lockorder_abort = prev; 101 #endif 102 } 103 104 /* Double lock would produce an undefined behavior. Thus, we only do that if 105 * DEBUG_LOCKORDER is activated to detect it. We don't want non-DEBUG_LOCKORDER 106 * build to produce tests that exhibit known undefined behavior. */ 107 #ifdef DEBUG_LOCKORDER 108 BOOST_AUTO_TEST_CASE(double_lock_mutex) 109 { 110 TestDoubleLock<Mutex>(/*should_throw=*/true); 111 } 112 113 BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex) 114 { 115 TestDoubleLock<RecursiveMutex>(/*should_throw=*/false); 116 } 117 #endif /* DEBUG_LOCKORDER */ 118 119 BOOST_AUTO_TEST_CASE(inconsistent_lock_order_detected) 120 { 121 #ifdef DEBUG_LOCKORDER 122 bool prev = g_debug_lockorder_abort; 123 g_debug_lockorder_abort = false; 124 #endif // DEBUG_LOCKORDER 125 126 RecursiveMutex rmutex1, rmutex2; 127 TestInconsistentLockOrderDetected(rmutex1, rmutex2); 128 // By checking lock order consistency (CheckLastCritical) before any unlocking (LeaveCritical) 129 // the lock tracking data must not have been broken by exception. 130 TestInconsistentLockOrderDetected(rmutex1, rmutex2); 131 132 Mutex mutex1, mutex2; 133 TestInconsistentLockOrderDetected(mutex1, mutex2); 134 // By checking lock order consistency (CheckLastCritical) before any unlocking (LeaveCritical) 135 // the lock tracking data must not have been broken by exception. 136 TestInconsistentLockOrderDetected(mutex1, mutex2); 137 138 #ifdef DEBUG_LOCKORDER 139 g_debug_lockorder_abort = prev; 140 #endif // DEBUG_LOCKORDER 141 } 142 143 BOOST_AUTO_TEST_SUITE_END()