Branch data Line data Source code
1 : : // Copyright (c) 2012-present 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/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 : : {
22 : : #ifdef DEBUG_LOCKORDER
23 : : BOOST_CHECK_EXCEPTION(LOCK2(mutex2, mutex1), std::logic_error, HasReason{"potential deadlock detected: mutex1 -> mutex2 -> mutex1"});
24 : : #else
25 [ + - ]: 4 : LOCK2(mutex2, mutex1);
26 : : #endif
27 [ + - ]: 4 : }
28 [ + - ]: 8 : BOOST_CHECK(LockStackEmpty());
29 : 4 : }
30 : :
31 : : #ifdef DEBUG_LOCKORDER
32 : : template <typename MutexType>
33 : : void TestDoubleLock2(MutexType& m)
34 : : {
35 : : LOCK(m);
36 : : }
37 : :
38 : : template <typename MutexType>
39 : : void TestDoubleLock(bool should_throw)
40 : : {
41 : : const bool prev = g_debug_lockorder_abort;
42 : : g_debug_lockorder_abort = false;
43 : :
44 : : MutexType m;
45 : : {
46 : : LOCK(m);
47 : : if (should_throw) {
48 : : BOOST_CHECK_EXCEPTION(TestDoubleLock2(m), std::logic_error,
49 : : HasReason("double lock detected"));
50 : : } else {
51 : : BOOST_CHECK_NO_THROW(TestDoubleLock2(m));
52 : : }
53 : : }
54 : : BOOST_CHECK(LockStackEmpty());
55 : :
56 : : g_debug_lockorder_abort = prev;
57 : : }
58 : : #endif /* DEBUG_LOCKORDER */
59 : :
60 : : template <typename MutexType>
61 : 4 : void TestInconsistentLockOrderDetected(MutexType& mutex1, MutexType& mutex2)
62 : : {
63 : : {
64 : 4 : WAIT_LOCK(mutex1, lock1);
65 [ + - ]: 4 : LOCK(mutex2);
66 : : #ifdef DEBUG_LOCKORDER
67 : : BOOST_CHECK_EXCEPTION(REVERSE_LOCK(lock1, mutex1), std::logic_error, HasReason("mutex1 was not most recent critical section locked"));
68 : : #endif // DEBUG_LOCKORDER
69 [ + - ]: 4 : }
70 [ + - ]: 8 : BOOST_CHECK(LockStackEmpty());
71 : 4 : }
72 : : } // namespace
73 : :
74 : : BOOST_AUTO_TEST_SUITE(sync_tests)
75 : :
76 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(potential_deadlock_detected)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
77 : : {
78 : : #ifdef DEBUG_LOCKORDER
79 : : bool prev = g_debug_lockorder_abort;
80 : : g_debug_lockorder_abort = false;
81 : : #endif
82 : :
83 : 1 : RecursiveMutex rmutex1, rmutex2;
84 : 1 : TestPotentialDeadLockDetected(rmutex1, rmutex2);
85 : : // The second test ensures that lock tracking data have not been broken by exception.
86 : 1 : TestPotentialDeadLockDetected(rmutex1, rmutex2);
87 : :
88 : 1 : Mutex mutex1, mutex2;
89 : 1 : TestPotentialDeadLockDetected(mutex1, mutex2);
90 : : // The second test ensures that lock tracking data have not been broken by exception.
91 : 1 : TestPotentialDeadLockDetected(mutex1, mutex2);
92 : :
93 : : #ifdef DEBUG_LOCKORDER
94 : : g_debug_lockorder_abort = prev;
95 : : #endif
96 : 1 : }
97 : :
98 : : /* Double lock would produce an undefined behavior. Thus, we only do that if
99 : : * DEBUG_LOCKORDER is activated to detect it. We don't want non-DEBUG_LOCKORDER
100 : : * build to produce tests that exhibit known undefined behavior. */
101 : : #ifdef DEBUG_LOCKORDER
102 : : BOOST_AUTO_TEST_CASE(double_lock_mutex)
103 : : {
104 : : TestDoubleLock<Mutex>(/*should_throw=*/true);
105 : : }
106 : :
107 : : BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex)
108 : : {
109 : : TestDoubleLock<RecursiveMutex>(/*should_throw=*/false);
110 : : }
111 : : #endif /* DEBUG_LOCKORDER */
112 : :
113 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(inconsistent_lock_order_detected)
+ - + - -
+ + - + -
+ - + - +
- - + + -
+ - + - +
- + - - +
+ - + - +
- + - + -
- + + - +
- + - + -
+ - - + +
- ]
114 : : {
115 : : #ifdef DEBUG_LOCKORDER
116 : : bool prev = g_debug_lockorder_abort;
117 : : g_debug_lockorder_abort = false;
118 : : #endif // DEBUG_LOCKORDER
119 : :
120 : 1 : RecursiveMutex rmutex1, rmutex2;
121 : 1 : TestInconsistentLockOrderDetected(rmutex1, rmutex2);
122 : : // By checking lock order consistency (CheckLastCritical) before any unlocking (LeaveCritical)
123 : : // the lock tracking data must not have been broken by exception.
124 : 1 : TestInconsistentLockOrderDetected(rmutex1, rmutex2);
125 : :
126 : 1 : Mutex mutex1, mutex2;
127 : 1 : TestInconsistentLockOrderDetected(mutex1, mutex2);
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(mutex1, mutex2);
131 : :
132 : : #ifdef DEBUG_LOCKORDER
133 : : g_debug_lockorder_abort = prev;
134 : : #endif // DEBUG_LOCKORDER
135 : 1 : }
136 : :
137 : : BOOST_AUTO_TEST_SUITE_END()
|