sync_tests.cpp raw
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 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>
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 void TestInconsistentLockOrderDetected(MutexType& mutex1, MutexType& mutex2)
67 {
68 {
69 WAIT_LOCK(mutex1, lock1);
70 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 }
75 BOOST_CHECK(LockStackEmpty());
76 }
77 } // namespace
78
79 BOOST_AUTO_TEST_SUITE(sync_tests)
80
81 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 RecursiveMutex rmutex1, rmutex2;
89 TestPotentialDeadLockDetected(rmutex1, rmutex2);
90 // The second test ensures that lock tracking data have not been broken by exception.
91 TestPotentialDeadLockDetected(rmutex1, rmutex2);
92
93 Mutex mutex1, mutex2;
94 TestPotentialDeadLockDetected(mutex1, mutex2);
95 // The second test ensures that lock tracking data have not been broken by exception.
96 TestPotentialDeadLockDetected(mutex1, mutex2);
97
98 #ifdef DEBUG_LOCKORDER
99 g_debug_lockorder_abort = prev;
100 #endif
101 }
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 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 RecursiveMutex rmutex1, rmutex2;
126 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 TestInconsistentLockOrderDetected(rmutex1, rmutex2);
130
131 Mutex mutex1, mutex2;
132 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 TestInconsistentLockOrderDetected(mutex1, mutex2);
136
137 #ifdef DEBUG_LOCKORDER
138 g_debug_lockorder_abort = prev;
139 #endif // DEBUG_LOCKORDER
140 }
141
142 BOOST_AUTO_TEST_SUITE_END()
143