Branch data Line data Source code
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 : 0 : void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2) 16 : : { 17 : : { 18 : 0 : LOCK2(mutex1, mutex2); 19 : 0 : } 20 : 0 : BOOST_CHECK(LockStackEmpty()); 21 : 0 : bool error_thrown = false; 22 : : try { 23 : 0 : LOCK2(mutex2, mutex1); 24 : 0 : } catch (const std::logic_error& e) { 25 : 0 : BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected: mutex1 -> mutex2 -> mutex1"); 26 : 0 : error_thrown = true; 27 : 0 : } 28 : 0 : BOOST_CHECK(LockStackEmpty()); 29 : : #ifdef DEBUG_LOCKORDER 30 : : BOOST_CHECK(error_thrown); 31 : : #else 32 : 0 : BOOST_CHECK(!error_thrown); 33 : : #endif 34 : 0 : } 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 : 0 : void TestInconsistentLockOrderDetected(MutexType& mutex1, MutexType& mutex2) NO_THREAD_SAFETY_ANALYSIS 68 : : { 69 : 0 : ENTER_CRITICAL_SECTION(mutex1); 70 : 0 : 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 : 0 : LEAVE_CRITICAL_SECTION(mutex2); 75 : 0 : LEAVE_CRITICAL_SECTION(mutex1); 76 : 0 : BOOST_CHECK(LockStackEmpty()); 77 : 0 : } 78 : : } // namespace 79 : : 80 : 0 : BOOST_AUTO_TEST_SUITE(sync_tests) 81 : : 82 : 0 : 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 : 0 : RecursiveMutex rmutex1, rmutex2; 90 : 0 : TestPotentialDeadLockDetected(rmutex1, rmutex2); 91 : : // The second test ensures that lock tracking data have not been broken by exception. 92 : 0 : TestPotentialDeadLockDetected(rmutex1, rmutex2); 93 : : 94 : 0 : Mutex mutex1, mutex2; 95 : 0 : TestPotentialDeadLockDetected(mutex1, mutex2); 96 : : // The second test ensures that lock tracking data have not been broken by exception. 97 : 0 : TestPotentialDeadLockDetected(mutex1, mutex2); 98 : : 99 : : #ifdef DEBUG_LOCKORDER 100 : : g_debug_lockorder_abort = prev; 101 : : #endif 102 : 0 : } 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 : 0 : 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 : 0 : RecursiveMutex rmutex1, rmutex2; 127 : 0 : 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 : 0 : TestInconsistentLockOrderDetected(rmutex1, rmutex2); 131 : : 132 : 0 : Mutex mutex1, mutex2; 133 : 0 : 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 : 0 : TestInconsistentLockOrderDetected(mutex1, mutex2); 137 : : 138 : : #ifdef DEBUG_LOCKORDER 139 : : g_debug_lockorder_abort = prev; 140 : : #endif // DEBUG_LOCKORDER 141 : 0 : } 142 : : 143 : 0 : BOOST_AUTO_TEST_SUITE_END()