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