Branch data 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 : : void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2)
# 16 : 8 : {
# 17 : 8 : {
# 18 : 8 : LOCK2(mutex1, mutex2);
# 19 : 8 : }
# 20 : 8 : BOOST_CHECK(LockStackEmpty());
# 21 : 8 : bool error_thrown = false;
# 22 : 8 : try {
# 23 : 8 : LOCK2(mutex2, mutex1);
# 24 : 8 : } catch (const std::logic_error& e) {
# 25 : 8 : BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected: mutex1 -> mutex2 -> mutex1");
# 26 : 8 : error_thrown = true;
# 27 : 8 : }
# 28 : 8 : BOOST_CHECK(LockStackEmpty());
# 29 : 8 : #ifdef DEBUG_LOCKORDER
# 30 : 8 : BOOST_CHECK(error_thrown);
# 31 : : #else
# 32 : : BOOST_CHECK(!error_thrown);
# 33 : : #endif
# 34 : 8 : }
# 35 : :
# 36 : : #ifdef DEBUG_LOCKORDER
# 37 : : template <typename MutexType>
# 38 : : void TestDoubleLock2(MutexType& m)
# 39 : 4 : {
# 40 : 4 : ENTER_CRITICAL_SECTION(m);
# 41 : 4 : LEAVE_CRITICAL_SECTION(m);
# 42 : 4 : }
# 43 : :
# 44 : : template <typename MutexType>
# 45 : : void TestDoubleLock(bool should_throw)
# 46 : 4 : {
# 47 : 4 : const bool prev = g_debug_lockorder_abort;
# 48 : 4 : g_debug_lockorder_abort = false;
# 49 : :
# 50 : 4 : MutexType m;
# 51 : 4 : ENTER_CRITICAL_SECTION(m);
# 52 [ + - ][ - + ]: 4 : if (should_throw) {
# 53 : 2 : BOOST_CHECK_EXCEPTION(TestDoubleLock2(m), std::logic_error,
# 54 : 2 : HasReason("double lock detected"));
# 55 : 2 : } else {
# 56 : 2 : BOOST_CHECK_NO_THROW(TestDoubleLock2(m));
# 57 : 2 : }
# 58 : 4 : LEAVE_CRITICAL_SECTION(m);
# 59 : :
# 60 : 4 : BOOST_CHECK(LockStackEmpty());
# 61 : :
# 62 : 4 : g_debug_lockorder_abort = prev;
# 63 : 4 : }
# 64 : : #endif /* DEBUG_LOCKORDER */
# 65 : :
# 66 : : template <typename MutexType>
# 67 : : void TestInconsistentLockOrderDetected(MutexType& mutex1, MutexType& mutex2) NO_THREAD_SAFETY_ANALYSIS
# 68 : 8 : {
# 69 : 8 : ENTER_CRITICAL_SECTION(mutex1);
# 70 : 8 : ENTER_CRITICAL_SECTION(mutex2);
# 71 : 8 : #ifdef DEBUG_LOCKORDER
# 72 : 8 : BOOST_CHECK_EXCEPTION(LEAVE_CRITICAL_SECTION(mutex1), std::logic_error, HasReason("mutex1 was not most recent critical section locked"));
# 73 : 8 : #endif // DEBUG_LOCKORDER
# 74 : 8 : LEAVE_CRITICAL_SECTION(mutex2);
# 75 : 8 : LEAVE_CRITICAL_SECTION(mutex1);
# 76 : 8 : BOOST_CHECK(LockStackEmpty());
# 77 : 8 : }
# 78 : : } // namespace
# 79 : :
# 80 : : BOOST_AUTO_TEST_SUITE(sync_tests)
# 81 : :
# 82 : : BOOST_AUTO_TEST_CASE(potential_deadlock_detected)
# 83 : 2 : {
# 84 : 2 : #ifdef DEBUG_LOCKORDER
# 85 : 2 : bool prev = g_debug_lockorder_abort;
# 86 : 2 : g_debug_lockorder_abort = false;
# 87 : 2 : #endif
# 88 : :
# 89 : 2 : RecursiveMutex rmutex1, rmutex2;
# 90 : 2 : TestPotentialDeadLockDetected(rmutex1, rmutex2);
# 91 : : // The second test ensures that lock tracking data have not been broken by exception.
# 92 : 2 : TestPotentialDeadLockDetected(rmutex1, rmutex2);
# 93 : :
# 94 : 2 : Mutex mutex1, mutex2;
# 95 : 2 : TestPotentialDeadLockDetected(mutex1, mutex2);
# 96 : : // The second test ensures that lock tracking data have not been broken by exception.
# 97 : 2 : TestPotentialDeadLockDetected(mutex1, mutex2);
# 98 : :
# 99 : 2 : #ifdef DEBUG_LOCKORDER
# 100 : 2 : g_debug_lockorder_abort = prev;
# 101 : 2 : #endif
# 102 : 2 : }
# 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 : 2 : {
# 110 : 2 : TestDoubleLock<Mutex>(true /* should throw */);
# 111 : 2 : }
# 112 : :
# 113 : : BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex)
# 114 : 2 : {
# 115 : 2 : TestDoubleLock<RecursiveMutex>(false /* should not throw */);
# 116 : 2 : }
# 117 : : #endif /* DEBUG_LOCKORDER */
# 118 : :
# 119 : : BOOST_AUTO_TEST_CASE(inconsistent_lock_order_detected)
# 120 : 2 : {
# 121 : 2 : #ifdef DEBUG_LOCKORDER
# 122 : 2 : bool prev = g_debug_lockorder_abort;
# 123 : 2 : g_debug_lockorder_abort = false;
# 124 : 2 : #endif // DEBUG_LOCKORDER
# 125 : :
# 126 : 2 : RecursiveMutex rmutex1, rmutex2;
# 127 : 2 : 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 : 2 : TestInconsistentLockOrderDetected(rmutex1, rmutex2);
# 131 : :
# 132 : 2 : Mutex mutex1, mutex2;
# 133 : 2 : 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 : 2 : TestInconsistentLockOrderDetected(mutex1, mutex2);
# 137 : :
# 138 : 2 : #ifdef DEBUG_LOCKORDER
# 139 : 2 : g_debug_lockorder_abort = prev;
# 140 : 2 : #endif // DEBUG_LOCKORDER
# 141 : 2 : }
# 142 : :
# 143 : : BOOST_AUTO_TEST_SUITE_END()
|