aboutsummaryrefslogtreecommitdiff
path: root/src/test/sync_tests.cpp
blob: 14145ced7e107f8686378a0176ba3d9502def5b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Copyright (c) 2012-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <sync.h>
#include <test/util/setup_common.h>

#include <boost/test/unit_test.hpp>
#include <boost/thread/mutex.hpp>

#include <mutex>

namespace {
template <typename MutexType>
void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2)
{
    {
        LOCK2(mutex1, mutex2);
    }
    BOOST_CHECK(LockStackEmpty());
    bool error_thrown = false;
    try {
        LOCK2(mutex2, mutex1);
    } catch (const std::logic_error& e) {
        BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected: mutex1 -> mutex2 -> mutex1");
        error_thrown = true;
    }
    BOOST_CHECK(LockStackEmpty());
    #ifdef DEBUG_LOCKORDER
    BOOST_CHECK(error_thrown);
    #else
    BOOST_CHECK(!error_thrown);
    #endif
}

#ifdef DEBUG_LOCKORDER
template <typename MutexType>
void TestDoubleLock2(MutexType& m)
{
    ENTER_CRITICAL_SECTION(m);
    LEAVE_CRITICAL_SECTION(m);
}

template <typename MutexType>
void TestDoubleLock(bool should_throw)
{
    const bool prev = g_debug_lockorder_abort;
    g_debug_lockorder_abort = false;

    MutexType m;
    ENTER_CRITICAL_SECTION(m);
    if (should_throw) {
        BOOST_CHECK_EXCEPTION(TestDoubleLock2(m), std::logic_error,
                              HasReason("double lock detected"));
    } else {
        BOOST_CHECK_NO_THROW(TestDoubleLock2(m));
    }
    LEAVE_CRITICAL_SECTION(m);

    BOOST_CHECK(LockStackEmpty());

    g_debug_lockorder_abort = prev;
}
#endif /* DEBUG_LOCKORDER */
} // namespace

BOOST_FIXTURE_TEST_SUITE(sync_tests, BasicTestingSetup)

BOOST_AUTO_TEST_CASE(potential_deadlock_detected)
{
    #ifdef DEBUG_LOCKORDER
    bool prev = g_debug_lockorder_abort;
    g_debug_lockorder_abort = false;
    #endif

    RecursiveMutex rmutex1, rmutex2;
    TestPotentialDeadLockDetected(rmutex1, rmutex2);
    // The second test ensures that lock tracking data have not been broken by exception.
    TestPotentialDeadLockDetected(rmutex1, rmutex2);

    Mutex mutex1, mutex2;
    TestPotentialDeadLockDetected(mutex1, mutex2);
    // The second test ensures that lock tracking data have not been broken by exception.
    TestPotentialDeadLockDetected(mutex1, mutex2);

    #ifdef DEBUG_LOCKORDER
    g_debug_lockorder_abort = prev;
    #endif
}

/* Double lock would produce an undefined behavior. Thus, we only do that if
 * DEBUG_LOCKORDER is activated to detect it. We don't want non-DEBUG_LOCKORDER
 * build to produce tests that exhibit known undefined behavior. */
#ifdef DEBUG_LOCKORDER
BOOST_AUTO_TEST_CASE(double_lock_mutex)
{
    TestDoubleLock<Mutex>(true /* should throw */);
}

BOOST_AUTO_TEST_CASE(double_lock_boost_mutex)
{
    TestDoubleLock<boost::mutex>(true /* should throw */);
}

BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex)
{
    TestDoubleLock<RecursiveMutex>(false /* should not throw */);
}
#endif /* DEBUG_LOCKORDER */

BOOST_AUTO_TEST_SUITE_END()