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 : : LOCK(m);
41 : : }
42 : :
43 : : template <typename MutexType>
44 : : void TestDoubleLock(bool should_throw)
45 : : {
46 : : const bool prev = g_debug_lockorder_abort;
47 : : g_debug_lockorder_abort = false;
48 : :
49 : : MutexType m;
50 : : {
51 : : LOCK(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 : : }
59 : : BOOST_CHECK(LockStackEmpty());
60 : :
61 : : g_debug_lockorder_abort = prev;
62 : : }
63 : : #endif /* DEBUG_LOCKORDER */
64 : :
65 : : template <typename MutexType>
66 : 4 : void TestInconsistentLockOrderDetected(MutexType& mutex1, MutexType& mutex2)
67 : : {
68 : : {
69 : 4 : WAIT_LOCK(mutex1, lock1);
70 [ + - ]: 4 : LOCK(mutex2);
71 : : #ifdef DEBUG_LOCKORDER
72 : : BOOST_CHECK_EXCEPTION(REVERSE_LOCK(lock1, mutex1), std::logic_error, HasReason("mutex1 was not most recent critical section locked"));
73 : : #endif // DEBUG_LOCKORDER
74 [ + - ]: 4 : }
75 [ + - ]: 8 : BOOST_CHECK(LockStackEmpty());
76 : 4 : }
77 : : } // namespace
78 : :
79 : : BOOST_AUTO_TEST_SUITE(sync_tests)
80 : :
81 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(potential_deadlock_detected)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
82 : : {
83 : : #ifdef DEBUG_LOCKORDER
84 : : bool prev = g_debug_lockorder_abort;
85 : : g_debug_lockorder_abort = false;
86 : : #endif
87 : :
88 : 1 : RecursiveMutex rmutex1, rmutex2;
89 : 1 : TestPotentialDeadLockDetected(rmutex1, rmutex2);
90 : : // The second test ensures that lock tracking data have not been broken by exception.
91 : 1 : TestPotentialDeadLockDetected(rmutex1, rmutex2);
92 : :
93 : 1 : Mutex mutex1, mutex2;
94 : 1 : TestPotentialDeadLockDetected(mutex1, mutex2);
95 : : // The second test ensures that lock tracking data have not been broken by exception.
96 : 1 : TestPotentialDeadLockDetected(mutex1, mutex2);
97 : :
98 : : #ifdef DEBUG_LOCKORDER
99 : : g_debug_lockorder_abort = prev;
100 : : #endif
101 : 1 : }
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 : : {
109 : : TestDoubleLock<Mutex>(/*should_throw=*/true);
110 : : }
111 : :
112 : : BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex)
113 : : {
114 : : TestDoubleLock<RecursiveMutex>(/*should_throw=*/false);
115 : : }
116 : : #endif /* DEBUG_LOCKORDER */
117 : :
118 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(inconsistent_lock_order_detected)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
119 : : {
120 : : #ifdef DEBUG_LOCKORDER
121 : : bool prev = g_debug_lockorder_abort;
122 : : g_debug_lockorder_abort = false;
123 : : #endif // DEBUG_LOCKORDER
124 : :
125 : 1 : RecursiveMutex rmutex1, rmutex2;
126 : 1 : 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 : 1 : TestInconsistentLockOrderDetected(rmutex1, rmutex2);
130 : :
131 : 1 : Mutex mutex1, mutex2;
132 : 1 : 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 : 1 : TestInconsistentLockOrderDetected(mutex1, mutex2);
136 : :
137 : : #ifdef DEBUG_LOCKORDER
138 : : g_debug_lockorder_abort = prev;
139 : : #endif // DEBUG_LOCKORDER
140 : 1 : }
141 : :
142 : : BOOST_AUTO_TEST_SUITE_END()
|