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