bitcoin/src/test/reverselock_tests.cpp
Cory Fields a201a99f8c thread-safety: fix annotations with REVERSE_LOCK
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
2025-06-16 18:09:14 +00:00

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()