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 : 4 : void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2)
16 : : {
17 : : {
18 [ + - ]: 4 : LOCK2(mutex1, mutex2);
19 [ + - ]: 4 : }
20 [ + - + - ]: 8 : BOOST_CHECK(LockStackEmpty());
21 : 4 : bool error_thrown = false;
22 : : try {
23 [ + - + - ]: 4 : LOCK2(mutex2, mutex1);
24 [ + - - - ]: 8 : } 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 : : }
28 [ + - ]: 8 : BOOST_CHECK(LockStackEmpty());
29 : : #ifdef DEBUG_LOCKORDER
30 : : BOOST_CHECK(error_thrown);
31 : : #else
32 [ + - ]: 8 : BOOST_CHECK(!error_thrown);
33 : : #endif
34 : : }
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 : 4 : void TestInconsistentLockOrderDetected(MutexType& mutex1, MutexType& mutex2) NO_THREAD_SAFETY_ANALYSIS
68 : : {
69 : 4 : ENTER_CRITICAL_SECTION(mutex1);
70 : 4 : 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 : 4 : LEAVE_CRITICAL_SECTION(mutex2);
75 : 4 : LEAVE_CRITICAL_SECTION(mutex1);
76 [ + - ]: 8 : BOOST_CHECK(LockStackEmpty());
77 : 4 : }
78 : : } // namespace
79 : :
80 : : BOOST_AUTO_TEST_SUITE(sync_tests)
81 : :
82 [ + - + - : 7 : 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 : : /* 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 [ + - + - : 7 : 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 : 1 : RecursiveMutex rmutex1, rmutex2;
127 : 1 : 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 : 1 : TestInconsistentLockOrderDetected(rmutex1, rmutex2);
131 : :
132 : 1 : Mutex mutex1, mutex2;
133 : 1 : 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 : 1 : TestInconsistentLockOrderDetected(mutex1, mutex2);
137 : :
138 : : #ifdef DEBUG_LOCKORDER
139 : : g_debug_lockorder_abort = prev;
140 : : #endif // DEBUG_LOCKORDER
141 : 1 : }
142 : :
143 : : BOOST_AUTO_TEST_SUITE_END()
|