Bitcoin Core  22.99.0
P2P Digital Currency
sync_tests.cpp
Go to the documentation of this file.
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>
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  }
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  }
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 {
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;
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  }
59 
61 
62  g_debug_lockorder_abort = prev;
63 }
64 #endif /* DEBUG_LOCKORDER */
65 
66 template <typename MutexType>
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);
77 }
78 } // namespace
79 
80 BOOST_AUTO_TEST_SUITE(sync_tests)
81 
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
108 BOOST_AUTO_TEST_CASE(double_lock_mutex)
109 {
110  TestDoubleLock<Mutex>(true /* should throw */);
111 }
112 
113 BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex)
114 {
115  TestDoubleLock<RecursiveMutex>(false /* should not throw */);
116 }
117 #endif /* DEBUG_LOCKORDER */
118 
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 
LOCK2
#define LOCK2(cs1, cs2)
Definition: sync.h:227
HasReason
BOOST_CHECK_EXCEPTION predicates to check the specific validation error.
Definition: setup_common.h:217
BOOST_AUTO_TEST_SUITE
BOOST_AUTO_TEST_SUITE(cuckoocache_tests)
Test Suite for CuckooCache.
setup_common.h
sync.h
AnnotatedMixin< std::recursive_mutex >
BOOST_AUTO_TEST_SUITE_END
BOOST_AUTO_TEST_SUITE_END()
ENTER_CRITICAL_SECTION
#define ENTER_CRITICAL_SECTION(cs)
Definition: sync.h:233
NO_THREAD_SAFETY_ANALYSIS
#define NO_THREAD_SAFETY_ANALYSIS
Definition: threadsafety.h:51
BOOST_CHECK_NO_THROW
#define BOOST_CHECK_NO_THROW(stmt)
Definition: object.cpp:28
LockStackEmpty
bool LockStackEmpty()
Definition: sync.h:81
LEAVE_CRITICAL_SECTION
#define LEAVE_CRITICAL_SECTION(cs)
Definition: sync.h:239
BOOST_CHECK
#define BOOST_CHECK(expr)
Definition: object.cpp:17
BOOST_CHECK_EQUAL
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(potential_deadlock_detected)
Definition: sync_tests.cpp:82