LCOV - code coverage report
Current view: top level - src/test - sync_tests.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 35 38 92.1 %
Date: 2026-06-25 07:23:51 Functions: 13 13 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2012-2021 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/setup_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           4 : void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2)
      16             : {
      17             :     {
      18           4 :         LOCK2(mutex1, mutex2);
      19           4 :     }
      20           4 :     BOOST_CHECK(LockStackEmpty());
      21           4 :     bool error_thrown = false;
      22             :     try {
      23           4 :         LOCK2(mutex2, mutex1);
      24           4 :     } catch (const std::logic_error& e) {
      25           0 :         BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected: mutex1 -> mutex2 -> mutex1");
      26           0 :         error_thrown = true;
      27           0 :     }
      28           4 :     BOOST_CHECK(LockStackEmpty());
      29             :     #ifdef DEBUG_LOCKORDER
      30             :     BOOST_CHECK(error_thrown);
      31             :     #else
      32           4 :     BOOST_CHECK(!error_thrown);
      33             :     #endif
      34           4 : }
      35             : 
      36             : template <typename MutexType>
      37           4 : void TestInconsistentLockOrderDetected(MutexType& mutex1, MutexType& mutex2) NO_THREAD_SAFETY_ANALYSIS
      38             : {
      39           4 :     ENTER_CRITICAL_SECTION(mutex1);
      40           4 :     ENTER_CRITICAL_SECTION(mutex2);
      41             : #ifdef DEBUG_LOCKORDER
      42             :     BOOST_CHECK_EXCEPTION(LEAVE_CRITICAL_SECTION(mutex1), std::logic_error, HasReason("mutex1 was not most recent critical section locked"));
      43             : #endif // DEBUG_LOCKORDER
      44           4 :     LEAVE_CRITICAL_SECTION(mutex2);
      45           4 :     LEAVE_CRITICAL_SECTION(mutex1);
      46           4 :     BOOST_CHECK(LockStackEmpty());
      47           4 : }
      48             : 
      49             : #ifdef DEBUG_LOCKORDER
      50             : template <typename MutexType>
      51             : void TestDoubleLock2(MutexType& m)
      52             : {
      53             :     ENTER_CRITICAL_SECTION(m);
      54             :     LEAVE_CRITICAL_SECTION(m);
      55             : }
      56             : 
      57             : template <typename MutexType>
      58             : void TestDoubleLock(bool should_throw)
      59             : {
      60             :     const bool prev = g_debug_lockorder_abort;
      61             :     g_debug_lockorder_abort = false;
      62             : 
      63             :     MutexType m;
      64             :     ENTER_CRITICAL_SECTION(m);
      65             :     if (should_throw) {
      66             :         BOOST_CHECK_EXCEPTION(TestDoubleLock2(m), std::logic_error,
      67             :                               HasReason("double lock detected"));
      68             :     } else {
      69             :         BOOST_CHECK_NO_THROW(TestDoubleLock2(m));
      70             :     }
      71             :     LEAVE_CRITICAL_SECTION(m);
      72             : 
      73             :     BOOST_CHECK(LockStackEmpty());
      74             : 
      75             :     g_debug_lockorder_abort = prev;
      76             : }
      77             : #endif /* DEBUG_LOCKORDER */
      78             : } // namespace
      79             : 
      80         146 : BOOST_AUTO_TEST_SUITE(sync_tests)
      81             : 
      82         148 : 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           1 :     RecursiveMutex rmutex1, rmutex2;
      90           1 :     TestPotentialDeadLockDetected(rmutex1, rmutex2);
      91             :     // The second test ensures that lock tracking data have not been broken by exception.
      92           1 :     TestPotentialDeadLockDetected(rmutex1, rmutex2);
      93             : 
      94           1 :     Mutex mutex1, mutex2;
      95           1 :     TestPotentialDeadLockDetected(mutex1, mutex2);
      96             :     // The second test ensures that lock tracking data have not been broken by exception.
      97           1 :     TestPotentialDeadLockDetected(mutex1, mutex2);
      98             : 
      99             :     #ifdef DEBUG_LOCKORDER
     100             :     g_debug_lockorder_abort = prev;
     101             :     #endif
     102           1 : }
     103             : 
     104         148 : BOOST_AUTO_TEST_CASE(inconsistent_lock_order_detected)
     105             : {
     106             : #ifdef DEBUG_LOCKORDER
     107             :     bool prev = g_debug_lockorder_abort;
     108             :     g_debug_lockorder_abort = false;
     109             : #endif // DEBUG_LOCKORDER
     110             : 
     111           1 :     RecursiveMutex rmutex1, rmutex2;
     112           1 :     TestInconsistentLockOrderDetected(rmutex1, rmutex2);
     113             :     // By checking lock order consistency (CheckLastCritical) before any unlocking (LeaveCritical)
     114             :     // the lock tracking data must not have been broken by exception.
     115           1 :     TestInconsistentLockOrderDetected(rmutex1, rmutex2);
     116             : 
     117           1 :     Mutex mutex1, mutex2;
     118           1 :     TestInconsistentLockOrderDetected(mutex1, mutex2);
     119             :     // By checking lock order consistency (CheckLastCritical) before any unlocking (LeaveCritical)
     120             :     // the lock tracking data must not have been broken by exception.
     121           1 :     TestInconsistentLockOrderDetected(mutex1, mutex2);
     122             : 
     123             : #ifdef DEBUG_LOCKORDER
     124             :     g_debug_lockorder_abort = prev;
     125             : #endif // DEBUG_LOCKORDER
     126           1 : }
     127             : 
     128             : /* Double lock would produce an undefined behavior. Thus, we only do that if
     129             :  * DEBUG_LOCKORDER is activated to detect it. We don't want non-DEBUG_LOCKORDER
     130             :  * build to produce tests that exhibit known undefined behavior. */
     131             : #ifdef DEBUG_LOCKORDER
     132             : BOOST_AUTO_TEST_CASE(double_lock_mutex)
     133             : {
     134             :     TestDoubleLock<Mutex>(true /* should throw */);
     135             : }
     136             : 
     137             : BOOST_AUTO_TEST_CASE(double_lock_recursive_mutex)
     138             : {
     139             :     TestDoubleLock<RecursiveMutex>(false /* should not throw */);
     140             : }
     141             : #endif /* DEBUG_LOCKORDER */
     142             : 
     143         146 : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.16