LCOV - code coverage report
Current view: top level - src - sync.h (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 82 99 82.8 %
Date: 2025-02-23 09:33:43 Functions: 10 11 90.9 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2017 The Bitcoin developers
       3             : // Copyright (c) 2017-2020 The PIVX Core developers
       4             : // Distributed under the MIT/X11 software license, see the accompanying
       5             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       6             : 
       7             : #ifndef PIVX_SYNC_H
       8             : #define PIVX_SYNC_H
       9             : 
      10             : #include "threadsafety.h"
      11             : #include "util/macros.h"
      12             : 
      13             : #include <condition_variable>
      14             : #include <mutex>
      15             : #include <string>
      16             : #include <thread>
      17             : 
      18             : 
      19             : /////////////////////////////////////////////////
      20             : //                                             //
      21             : // THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE //
      22             : //                                             //
      23             : /////////////////////////////////////////////////
      24             : 
      25             : /*
      26             : RecursiveMutex mutex;
      27             :     std::recursive_mutex mutex;
      28             : 
      29             : LOCK(mutex);
      30             :     std::unique_lock<std::recursive_mutex> criticalblock(mutex);
      31             : 
      32             : LOCK2(mutex1, mutex2);
      33             :     std::unique_lock<std::recursive_mutex> criticalblock1(mutex1);
      34             :     std::unique_lock<std::recursive_mutex> criticalblock2(mutex2);
      35             : 
      36             : TRY_LOCK(mutex, name);
      37             :     std::unique_lock<std::recursive_mutex> name(mutex, std::try_to_lock_t);
      38             : 
      39             : ENTER_CRITICAL_SECTION(mutex); // no RAII
      40             :     mutex.lock();
      41             : 
      42             : LEAVE_CRITICAL_SECTION(mutex); // no RAII
      43             :     mutex.unlock();
      44             :  */
      45             : 
      46             : ///////////////////////////////
      47             : //                           //
      48             : // THE ACTUAL IMPLEMENTATION //
      49             : //                           //
      50             : ///////////////////////////////
      51             : 
      52             : #ifdef DEBUG_LOCKORDER
      53             : void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
      54             : void LeaveCritical();
      55             : void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
      56             : std::string LocksHeld();
      57             : void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
      58             : void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
      59             : void DeleteLock(void* cs);
      60             : 
      61             : /**
      62             :  * Call abort() if a potential lock order deadlock bug is detected, instead of
      63             :  * just logging information and throwing a logic_error. Defaults to true, and
      64             :  * set to false in DEBUG_LOCKORDER unit tests.
      65             :  */
      66             : extern bool g_debug_lockorder_abort;
      67             : #else
      68   119635706 : void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
      69      618725 : void static inline LeaveCritical() {}
      70      618726 : void static inline CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
      71    64667537 : void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
      72      204640 : void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
      73       30084 : void static inline DeleteLock(void* cs) {}
      74             : #endif
      75             : #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
      76             : #define AssertLockNotHeld(cs) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs)
      77             : 
      78             : /**
      79             :  * Template mixin that adds -Wthread-safety locking annotations and lock order
      80             :  * checking to a subset of the mutex API.
      81             :  */
      82             : template <typename PARENT>
      83       32139 : class LOCKABLE AnnotatedMixin : public PARENT
      84             : {
      85             : public:
      86       30084 :     ~AnnotatedMixin() {
      87       29042 :         DeleteLock((void*)this);
      88       19612 :     }
      89             : 
      90           7 :     void lock() EXCLUSIVE_LOCK_FUNCTION()
      91             :     {
      92           7 :         PARENT::lock();
      93             :     }
      94             : 
      95           7 :     void unlock() UNLOCK_FUNCTION()
      96             :     {
      97          14 :         PARENT::unlock();
      98             :     }
      99             : 
     100             :     bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true)
     101             :     {
     102             :         return PARENT::try_lock();
     103             :     }
     104             : 
     105             :     using UniqueLock = std::unique_lock<PARENT>;
     106             : };
     107             : 
     108             : /**
     109             :  * Wrapped mutex: supports recursive locking, but no waiting
     110             :  * TODO: We should move away from using the recursive lock by default.
     111             :  */
     112             : using RecursiveMutex = AnnotatedMixin<std::recursive_mutex>;
     113             : 
     114             : /** Wrapped mutex: supports waiting but not recursive locking */
     115             : typedef AnnotatedMixin<std::mutex> Mutex;
     116             : 
     117             : #ifdef DEBUG_LOCKCONTENTION
     118             : void PrintLockContention(const char* pszName, const char* pszFile, int nLine);
     119             : #endif
     120             : 
     121             : /** Wrapper around std::unique_lock style lock for Mutex. */
     122             : template <typename Mutex, typename Base = typename Mutex::UniqueLock>
     123             : class SCOPED_LOCKABLE UniqueLock  : public Base
     124             : {
     125             : private:
     126   117040807 :     void Enter(const char* pszName, const char* pszFile, int nLine)
     127             :     {
     128   117040807 :         EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex()));
     129             : #ifdef DEBUG_LOCKCONTENTION
     130             :         if (!Base::try_lock()) {
     131             :             PrintLockContention(pszName, pszFile, nLine);
     132             : #endif
     133   117040807 :         Base::lock();
     134             : #ifdef DEBUG_LOCKCONTENTION
     135             :         }
     136             : #endif
     137   117040807 :     }
     138             : 
     139     1976126 :     bool TryEnter(const char* pszName, const char* pszFile, int nLine)
     140             :     {
     141     1976126 :         EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex()), true);
     142     1976126 :         Base::try_lock();
     143   119016997 :         if (!Base::owns_lock())
     144             :             LeaveCritical();
     145   119016997 :         return Base::owns_lock();
     146             :     }
     147             : 
     148             : public:
     149   118913546 :     UniqueLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
     150             :     {
     151   118913546 :         if (fTry)
     152   118913546 :             TryEnter(pszName, pszFile, nLine);
     153             :         else
     154   118913546 :             Enter(pszName, pszFile, nLine);
     155   118913546 :     }
     156             : 
     157      103439 :     UniqueLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
     158      103439 :     {
     159      103439 :         if (!pmutexIn) return;
     160             : 
     161      103429 :         *static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock);
     162      103429 :         if (fTry)
     163      103439 :             TryEnter(pszName, pszFile, nLine);
     164             :         else
     165      103439 :             Enter(pszName, pszFile, nLine);
     166             :     }
     167             : 
     168   119635804 :     ~UniqueLock() UNLOCK_FUNCTION()
     169             :     {
     170   119016598 :         if (Base::owns_lock())
     171             :             LeaveCritical();
     172   218362797 :     }
     173             : 
     174     1976130 :     operator bool()
     175             :     {
     176     1976130 :         return Base::owns_lock();
     177             :     }
     178             : 
     179             : protected:
     180             :     // needed for reverse_lock
     181      618726 :     UniqueLock() { }
     182             : 
     183             : public:
     184             :     /**
     185             :      * An RAII-style reverse lock. Unlocks on construction and locks on destruction.
     186             :      */
     187             :     class reverse_lock {
     188             :     public:
     189      618728 :         explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
     190      618726 :             CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
     191      618726 :             lock.unlock();
     192             :             LeaveCritical();
     193      618725 :             lock.swap(templock);
     194      618725 :         }
     195             : 
     196      618725 :         ~reverse_lock() {
     197      618725 :             templock.swap(lock);
     198      618725 :             EnterCritical(lockname.c_str(), file.c_str(), line, (void*)lock.mutex());
     199      618725 :             lock.lock();
     200      618729 :         }
     201             : 
     202             :      private:
     203             :         reverse_lock(reverse_lock const&);
     204             :         reverse_lock& operator=(reverse_lock const&);
     205             : 
     206             :         UniqueLock& lock;
     207             :         UniqueLock templock;
     208             :         std::string lockname;
     209             :         const std::string file;
     210             :         const int line;
     211             :      };
     212             :      friend class reverse_lock;
     213             : };
     214             : 
     215             : #define REVERSE_LOCK(g) decltype(g)::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__)
     216             : 
     217             : template<typename MutexArg>
     218             : using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>;
     219             : 
     220             : #define LOCK(cs) DebugLock<decltype(cs)> PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
     221             : #define LOCK2(cs1, cs2)                                               \
     222             :     DebugLock<decltype(cs1)> criticalblock1(cs1, #cs1, __FILE__, __LINE__); \
     223             :     DebugLock<decltype(cs2)> criticalblock2(cs2, #cs2, __FILE__, __LINE__);
     224             : #define TRY_LOCK(cs, name) DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__, true)
     225             : #define WAIT_LOCK(cs, name) DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__)
     226             : 
     227             : #define ENTER_CRITICAL_SECTION(cs)                            \
     228             :     {                                                         \
     229             :         EnterCritical(#cs, __FILE__, __LINE__, (void*)(&cs)); \
     230             :         (cs).lock();                                          \
     231             :     }
     232             : 
     233             : #define LEAVE_CRITICAL_SECTION(cs) \
     234             :     {                              \
     235             :         (cs).unlock();             \
     236             :         LeaveCritical();           \
     237             :     }
     238             : 
     239             : //! Run code while locking a mutex.
     240             : //!
     241             : //! Examples:
     242             : //!
     243             : //!   WITH_LOCK(cs, shared_val = shared_val + 1);
     244             : //!
     245             : //!   int val = WITH_LOCK(cs, return shared_val);
     246             : //!
     247             : #define WITH_LOCK(cs, code) [&] { LOCK(cs); code; }()
     248             : 
     249         710 : class CSemaphore
     250             : {
     251             : private:
     252             :     std::condition_variable condition;
     253             :     std::mutex mutex;
     254             :     int value;
     255             : 
     256             : public:
     257         710 :     explicit CSemaphore(int init) : value(init) {}
     258             : 
     259       47487 :     void wait()
     260             :     {
     261       47487 :         std::unique_lock<std::mutex> lock(mutex);
     262       47487 :         condition.wait(lock, [&]() { return value >= 1; });
     263       47487 :         value--;
     264       47487 :     }
     265             : 
     266           0 :     bool try_wait()
     267             :     {
     268           0 :         std::lock_guard<std::mutex> lock(mutex);
     269           0 :         if (value < 1)
     270             :             return false;
     271           0 :         value--;
     272           0 :         return true;
     273             :     }
     274             : 
     275       59202 :     void post()
     276             :     {
     277       59202 :         {
     278       59202 :             std::lock_guard<std::mutex> lock(mutex);
     279       59202 :             value++;
     280             :         }
     281       59202 :         condition.notify_one();
     282       59202 :     }
     283             : };
     284             : 
     285             : /** RAII-style semaphore lock */
     286             : class CSemaphoreGrant
     287             : {
     288             : private:
     289             :     CSemaphore* sem;
     290             :     bool fHaveGrant;
     291             : 
     292             : public:
     293       47487 :     void Acquire()
     294             :     {
     295       47487 :         if (fHaveGrant)
     296             :             return;
     297       47487 :         sem->wait();
     298       47487 :         fHaveGrant = true;
     299             :     }
     300             : 
     301       49627 :     void Release()
     302             :     {
     303         676 :         if (!fHaveGrant)
     304             :             return;
     305           0 :         sem->post();
     306       47487 :         fHaveGrant = false;
     307             :     }
     308             : 
     309           1 :     bool TryAcquire()
     310             :     {
     311           1 :         if (!fHaveGrant && sem->try_wait())
     312           0 :             fHaveGrant = true;
     313           1 :         return fHaveGrant;
     314             :     }
     315             : 
     316           0 :     void MoveTo(CSemaphoreGrant& grant)
     317             :     {
     318           0 :         grant.Release();
     319           0 :         grant.sem = sem;
     320           0 :         grant.fHaveGrant = fHaveGrant;
     321           0 :         fHaveGrant = false;
     322           0 :     }
     323             : 
     324        1464 :     CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {}
     325             : 
     326       47487 :     explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false)
     327             :     {
     328       47487 :         if (fTry)
     329           0 :             TryAcquire();
     330             :         else
     331       47487 :             Acquire();
     332       47487 :     }
     333             : 
     334       48951 :     ~CSemaphoreGrant()
     335        1464 :     {
     336       96438 :         Release();
     337           0 :     }
     338             : 
     339           0 :     operator bool() const
     340             :     {
     341           0 :         return fHaveGrant;
     342             :     }
     343             : };
     344             : 
     345             : #endif // PIVX_SYNC_H

Generated by: LCOV version 1.14