mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-02-06 21:51:14 +00:00
Without proper annotations, clang thinks that mutexes are still held for the duration of a reverse_lock. This could lead to subtle bugs as EXCLUSIVE_LOCKS_REQUIRED(foo) passes when it shouldn't. As mentioned in the docs [0], clang's thread-safety analyzer is unable to deal with aliases of mutexes, so it is not possible to use the lock's copy of the mutex for that purpose. Instead, the original mutex needs to be passed back to the reverse_lock for the sake of thread-safety analysis, but it is not actually used otherwise. [0]: https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
94 lines
2.2 KiB
C++
94 lines
2.2 KiB
C++
// Copyright (c) 2015-2021 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 <stdexcept>
|
|
|
|
BOOST_AUTO_TEST_SUITE(reverselock_tests)
|
|
|
|
BOOST_AUTO_TEST_CASE(reverselock_basics)
|
|
{
|
|
Mutex mutex;
|
|
WAIT_LOCK(mutex, lock);
|
|
|
|
BOOST_CHECK(lock.owns_lock());
|
|
{
|
|
REVERSE_LOCK(lock, mutex);
|
|
BOOST_CHECK(!lock.owns_lock());
|
|
}
|
|
BOOST_CHECK(lock.owns_lock());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(reverselock_multiple)
|
|
{
|
|
Mutex mutex2;
|
|
Mutex mutex;
|
|
WAIT_LOCK(mutex2, lock2);
|
|
WAIT_LOCK(mutex, lock);
|
|
|
|
// Make sure undoing two locks succeeds
|
|
{
|
|
REVERSE_LOCK(lock, mutex);
|
|
BOOST_CHECK(!lock.owns_lock());
|
|
REVERSE_LOCK(lock2, mutex2);
|
|
BOOST_CHECK(!lock2.owns_lock());
|
|
}
|
|
BOOST_CHECK(lock.owns_lock());
|
|
BOOST_CHECK(lock2.owns_lock());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_CASE(reverselock_errors)
|
|
{
|
|
Mutex mutex2;
|
|
Mutex mutex;
|
|
WAIT_LOCK(mutex2, lock2);
|
|
WAIT_LOCK(mutex, lock);
|
|
|
|
#ifdef DEBUG_LOCKORDER
|
|
bool prev = g_debug_lockorder_abort;
|
|
g_debug_lockorder_abort = false;
|
|
|
|
// Make sure trying to reverse lock a previous lock fails
|
|
BOOST_CHECK_EXCEPTION(REVERSE_LOCK(lock2, mutex2), std::logic_error, HasReason("lock2 was not most recent critical section locked"));
|
|
BOOST_CHECK(lock2.owns_lock());
|
|
|
|
g_debug_lockorder_abort = prev;
|
|
#endif
|
|
|
|
// Make sure trying to reverse lock an unlocked lock fails
|
|
lock.unlock();
|
|
|
|
BOOST_CHECK(!lock.owns_lock());
|
|
|
|
bool failed = false;
|
|
try {
|
|
REVERSE_LOCK(lock, mutex);
|
|
} catch(...) {
|
|
failed = true;
|
|
}
|
|
|
|
BOOST_CHECK(failed);
|
|
BOOST_CHECK(!lock.owns_lock());
|
|
|
|
// Locking the original lock after it has been taken by a reverse lock
|
|
// makes no sense. Ensure that the original lock no longer owns the lock
|
|
// after giving it to a reverse one.
|
|
|
|
lock.lock();
|
|
BOOST_CHECK(lock.owns_lock());
|
|
{
|
|
REVERSE_LOCK(lock, mutex);
|
|
BOOST_CHECK(!lock.owns_lock());
|
|
}
|
|
|
|
BOOST_CHECK(failed);
|
|
BOOST_CHECK(lock.owns_lock());
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|