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 #include <stdexcept>
12
13 namespace {
14 template <typename MutexType>
TestPotentialDeadLockDetected(MutexType & mutex1,MutexType & mutex2)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>
TestDoubleLock2(MutexType & m)38 void TestDoubleLock2(MutexType& m)
39 {
40 ENTER_CRITICAL_SECTION(m);
41 LEAVE_CRITICAL_SECTION(m);
42 }
43
44 template <typename MutexType>
TestDoubleLock(bool should_throw)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>
TestInconsistentLockOrderDetected(MutexType & mutex1,MutexType & mutex2)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
BOOST_AUTO_TEST_CASE(potential_deadlock_detected)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
BOOST_AUTO_TEST_CASE(double_lock_mutex)108 BOOST_AUTO_TEST_CASE(double_lock_mutex)
109 {
110 TestDoubleLock<Mutex>(true /* should throw */);
111 }
112
BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex)113 BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex)
114 {
115 TestDoubleLock<RecursiveMutex>(false /* should not throw */);
116 }
117 #endif /* DEBUG_LOCKORDER */
118
BOOST_AUTO_TEST_CASE(inconsistent_lock_order_detected)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()
144