LCOV - code coverage report
Current view: top level - src - sync.h (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 50 113 44.2 %
Date: 2023-09-26 12:08:55 Functions: 35 61 57.4 %

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2022 The Bitcoin Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #ifndef BITCOIN_SYNC_H
       7             : #define BITCOIN_SYNC_H
       8             : 
       9             : #ifdef DEBUG_LOCKCONTENTION
      10             : #include <logging.h>
      11             : #include <logging/timer.h>
      12             : #endif
      13             : 
      14             : #include <threadsafety.h> // IWYU pragma: export
      15             : #include <util/macros.h>
      16             : 
      17             : #include <condition_variable>
      18             : #include <mutex>
      19             : #include <string>
      20             : #include <thread>
      21             : 
      22             : ////////////////////////////////////////////////
      23             : //                                            //
      24             : // THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE //
      25             : //                                            //
      26             : ////////////////////////////////////////////////
      27             : 
      28             : /*
      29             : RecursiveMutex mutex;
      30             :     std::recursive_mutex mutex;
      31             : 
      32             : LOCK(mutex);
      33             :     std::unique_lock<std::recursive_mutex> criticalblock(mutex);
      34             : 
      35             : LOCK2(mutex1, mutex2);
      36             :     std::unique_lock<std::recursive_mutex> criticalblock1(mutex1);
      37             :     std::unique_lock<std::recursive_mutex> criticalblock2(mutex2);
      38             : 
      39             : TRY_LOCK(mutex, name);
      40             :     std::unique_lock<std::recursive_mutex> name(mutex, std::try_to_lock_t);
      41             : 
      42             : ENTER_CRITICAL_SECTION(mutex); // no RAII
      43             :     mutex.lock();
      44             : 
      45             : LEAVE_CRITICAL_SECTION(mutex); // no RAII
      46             :     mutex.unlock();
      47             :  */
      48             : 
      49             : ///////////////////////////////
      50             : //                           //
      51             : // THE ACTUAL IMPLEMENTATION //
      52             : //                           //
      53             : ///////////////////////////////
      54             : 
      55             : #ifdef DEBUG_LOCKORDER
      56             : template <typename MutexType>
      57             : void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false);
      58             : void LeaveCritical();
      59             : void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
      60             : std::string LocksHeld();
      61             : template <typename MutexType>
      62             : void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs);
      63             : template <typename MutexType>
      64             : void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs);
      65             : void DeleteLock(void* cs);
      66             : bool LockStackEmpty();
      67             : 
      68             : /**
      69             :  * Call abort() if a potential lock order deadlock bug is detected, instead of
      70             :  * just logging information and throwing a logic_error. Defaults to true, and
      71             :  * set to false in DEBUG_LOCKORDER unit tests.
      72             :  */
      73             : extern bool g_debug_lockorder_abort;
      74             : #else
      75             : template <typename MutexType>
      76     1635071 : inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false) {}
      77     2815397 : inline void LeaveCritical() {}
      78       25332 : inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
      79             : template <typename MutexType>
      80     2143392 : inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs) {}
      81             : template <typename MutexType>
      82       13537 : void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs) {}
      83       48163 : inline void DeleteLock(void* cs) {}
      84             : inline bool LockStackEmpty() { return true; }
      85             : #endif
      86             : 
      87             : /**
      88             :  * Template mixin that adds -Wthread-safety locking annotations and lock order
      89             :  * checking to a subset of the mutex API.
      90             :  */
      91             : template <typename PARENT>
      92             : class LOCKABLE AnnotatedMixin : public PARENT
      93             : {
      94             : public:
      95       48163 :     ~AnnotatedMixin() {
      96       48163 :         DeleteLock((void*)this);
      97       48163 :     }
      98             : 
      99        5259 :     void lock() EXCLUSIVE_LOCK_FUNCTION()
     100             :     {
     101        5259 :         PARENT::lock();
     102        5259 :     }
     103             : 
     104        5259 :     void unlock() UNLOCK_FUNCTION()
     105             :     {
     106        5259 :         PARENT::unlock();
     107        5259 :     }
     108             : 
     109             :     bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true)
     110             :     {
     111             :         return PARENT::try_lock();
     112             :     }
     113             : 
     114             :     using unique_lock = std::unique_lock<PARENT>;
     115             : #ifdef __clang__
     116             :     //! For negative capabilities in the Clang Thread Safety Analysis.
     117             :     //! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in conjunction
     118             :     //! with the ! operator, to indicate that a mutex should not be held.
     119             :     const AnnotatedMixin& operator!() const { return *this; }
     120             : #endif // __clang__
     121             : };
     122             : 
     123             : /**
     124             :  * Wrapped mutex: supports recursive locking, but no waiting
     125             :  * TODO: We should move away from using the recursive lock by default.
     126             :  */
     127             : using RecursiveMutex = AnnotatedMixin<std::recursive_mutex>;
     128             : 
     129             : /** Wrapped mutex: supports waiting but not recursive locking */
     130             : using Mutex = AnnotatedMixin<std::mutex>;
     131             : 
     132             : /** Different type to mark Mutex at global scope
     133             :  *
     134             :  * Thread safety analysis can't handle negative assertions about mutexes
     135             :  * with global scope well, so mark them with a separate type, and
     136             :  * eventually move all the mutexes into classes so they are not globally
     137             :  * visible.
     138             :  *
     139             :  * See: https://github.com/bitcoin/bitcoin/pull/20272#issuecomment-720755781
     140             :  */
     141             : class GlobalMutex : public Mutex { };
     142             : 
     143             : #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
     144             : 
     145         203 : inline void AssertLockNotHeldInline(const char* name, const char* file, int line, Mutex* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) { AssertLockNotHeldInternal(name, file, line, cs); }
     146       13334 : inline void AssertLockNotHeldInline(const char* name, const char* file, int line, RecursiveMutex* cs) LOCKS_EXCLUDED(cs) { AssertLockNotHeldInternal(name, file, line, cs); }
     147             : inline void AssertLockNotHeldInline(const char* name, const char* file, int line, GlobalMutex* cs) LOCKS_EXCLUDED(cs) { AssertLockNotHeldInternal(name, file, line, cs); }
     148             : #define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs)
     149             : 
     150             : /** Wrapper around std::unique_lock style lock for MutexType. */
     151             : template <typename MutexType>
     152             : class SCOPED_LOCKABLE UniqueLock : public MutexType::unique_lock
     153             : {
     154             : private:
     155             :     using Base = typename MutexType::unique_lock;
     156             : 
     157     1609740 :     void Enter(const char* pszName, const char* pszFile, int nLine)
     158             :     {
     159     1609740 :         EnterCritical(pszName, pszFile, nLine, Base::mutex());
     160             : #ifdef DEBUG_LOCKCONTENTION
     161             :         if (Base::try_lock()) return;
     162             :         LOG_TIME_MICROS_WITH_CATEGORY(strprintf("lock contention %s, %s:%d", pszName, pszFile, nLine), BCLog::LOCK);
     163             : #endif
     164     1609740 :         Base::lock();
     165     1609740 :     }
     166             : 
     167           0 :     bool TryEnter(const char* pszName, const char* pszFile, int nLine)
     168             :     {
     169           0 :         EnterCritical(pszName, pszFile, nLine, Base::mutex(), true);
     170           0 :         if (Base::try_lock()) {
     171           0 :             return true;
     172             :         }
     173           0 :         LeaveCritical();
     174           0 :         return false;
     175           0 :     }
     176             : 
     177             : public:
     178     1608527 :     UniqueLock(MutexType& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
     179             :     {
     180     1608527 :         if (fTry)
     181           0 :             TryEnter(pszName, pszFile, nLine);
     182             :         else
     183     1608527 :             Enter(pszName, pszFile, nLine);
     184     1608527 :     }
     185             : 
     186         201 :     UniqueLock(MutexType* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
     187             :     {
     188         201 :         if (!pmutexIn) return;
     189             : 
     190         201 :         *static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock);
     191         201 :         if (fTry)
     192           0 :             TryEnter(pszName, pszFile, nLine);
     193             :         else
     194         201 :             Enter(pszName, pszFile, nLine);
     195         201 :     }
     196             : 
     197     1629813 :     ~UniqueLock() UNLOCK_FUNCTION()
     198             :     {
     199     1629813 :         if (Base::owns_lock())
     200     1609743 :             LeaveCritical();
     201     1629813 :     }
     202             : 
     203           0 :     operator bool()
     204             :     {
     205           0 :         return Base::owns_lock();
     206             :     }
     207             : 
     208             : protected:
     209             :     // needed for reverse_lock
     210       20073 :     UniqueLock() { }
     211             : 
     212             : public:
     213             :     /**
     214             :      * An RAII-style reverse lock. Unlocks on construction and locks on destruction.
     215             :      */
     216             :     class reverse_lock {
     217             :     public:
     218       20073 :         explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
     219       20073 :             CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
     220       20073 :             lock.unlock();
     221       20073 :             LeaveCritical();
     222       20073 :             lock.swap(templock);
     223       20073 :         }
     224             : 
     225       20073 :         ~reverse_lock() {
     226       20073 :             templock.swap(lock);
     227       20073 :             EnterCritical(lockname.c_str(), file.c_str(), line, lock.mutex());
     228       20073 :             lock.lock();
     229       20073 :         }
     230             : 
     231             :      private:
     232             :         reverse_lock(reverse_lock const&);
     233             :         reverse_lock& operator=(reverse_lock const&);
     234             : 
     235             :         UniqueLock& lock;
     236             :         UniqueLock templock;
     237             :         std::string lockname;
     238             :         const std::string file;
     239             :         const int line;
     240             :      };
     241             :      friend class reverse_lock;
     242             : };
     243             : 
     244             : #define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, #g, __FILE__, __LINE__)
     245             : 
     246             : // When locking a Mutex, require negative capability to ensure the lock
     247             : // is not already held
     248      303500 : inline Mutex& MaybeCheckNotHeld(Mutex& cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) LOCK_RETURNED(cs) { return cs; }
     249             : inline Mutex* MaybeCheckNotHeld(Mutex* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) LOCK_RETURNED(cs) { return cs; }
     250             : 
     251             : // When locking a GlobalMutex or RecursiveMutex, just check it is not
     252             : // locked in the surrounding scope.
     253             : template <typename MutexType>
     254     1357755 : inline MutexType& MaybeCheckNotHeld(MutexType& m) LOCKS_EXCLUDED(m) LOCK_RETURNED(m) { return m; }
     255             : template <typename MutexType>
     256         201 : inline MutexType* MaybeCheckNotHeld(MutexType* m) LOCKS_EXCLUDED(m) LOCK_RETURNED(m) { return m; }
     257             : 
     258             : #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
     259             : #define LOCK2(cs1, cs2)                                               \
     260             :     UniqueLock criticalblock1(MaybeCheckNotHeld(cs1), #cs1, __FILE__, __LINE__); \
     261             :     UniqueLock criticalblock2(MaybeCheckNotHeld(cs2), #cs2, __FILE__, __LINE__)
     262             : #define TRY_LOCK(cs, name) UniqueLock name(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__, true)
     263             : #define WAIT_LOCK(cs, name) UniqueLock name(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
     264             : 
     265             : #define ENTER_CRITICAL_SECTION(cs)                            \
     266             :     {                                                         \
     267             :         EnterCritical(#cs, __FILE__, __LINE__, &cs); \
     268             :         (cs).lock();                                          \
     269             :     }
     270             : 
     271             : #define LEAVE_CRITICAL_SECTION(cs)                                          \
     272             :     {                                                                       \
     273             :         std::string lockname;                                               \
     274             :         CheckLastCritical((void*)(&cs), lockname, #cs, __FILE__, __LINE__); \
     275             :         (cs).unlock();                                                      \
     276             :         LeaveCritical();                                                    \
     277             :     }
     278             : 
     279             : //! Run code while locking a mutex.
     280             : //!
     281             : //! Examples:
     282             : //!
     283             : //!   WITH_LOCK(cs, shared_val = shared_val + 1);
     284             : //!
     285             : //!   int val = WITH_LOCK(cs, return shared_val);
     286             : //!
     287             : //! Note:
     288             : //!
     289             : //! Since the return type deduction follows that of decltype(auto), while the
     290             : //! deduced type of:
     291             : //!
     292             : //!   WITH_LOCK(cs, return {int i = 1; return i;});
     293             : //!
     294             : //! is int, the deduced type of:
     295             : //!
     296             : //!   WITH_LOCK(cs, return {int j = 1; return (j);});
     297             : //!
     298             : //! is &int, a reference to a local variable
     299             : //!
     300             : //! The above is detectable at compile-time with the -Wreturn-local-addr flag in
     301             : //! gcc and the -Wreturn-stack-address flag in clang, both enabled by default.
     302             : #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }())
     303             : 
     304             : class CSemaphore
     305             : {
     306             : private:
     307             :     std::condition_variable condition;
     308             :     std::mutex mutex;
     309             :     int value;
     310             : 
     311             : public:
     312           0 :     explicit CSemaphore(int init) : value(init) {}
     313             : 
     314           0 :     void wait()
     315             :     {
     316           0 :         std::unique_lock<std::mutex> lock(mutex);
     317           0 :         condition.wait(lock, [&]() { return value >= 1; });
     318           0 :         value--;
     319           0 :     }
     320             : 
     321           0 :     bool try_wait()
     322             :     {
     323           0 :         std::lock_guard<std::mutex> lock(mutex);
     324           0 :         if (value < 1)
     325           0 :             return false;
     326           0 :         value--;
     327           0 :         return true;
     328           0 :     }
     329             : 
     330           0 :     void post()
     331             :     {
     332             :         {
     333           0 :             std::lock_guard<std::mutex> lock(mutex);
     334           0 :             value++;
     335           0 :         }
     336           0 :         condition.notify_one();
     337           0 :     }
     338             : };
     339             : 
     340             : /** RAII-style semaphore lock */
     341             : class CSemaphoreGrant
     342             : {
     343             : private:
     344             :     CSemaphore* sem;
     345             :     bool fHaveGrant;
     346             : 
     347             : public:
     348           0 :     void Acquire()
     349             :     {
     350           0 :         if (fHaveGrant)
     351           0 :             return;
     352           0 :         sem->wait();
     353           0 :         fHaveGrant = true;
     354           0 :     }
     355             : 
     356           0 :     void Release()
     357             :     {
     358           0 :         if (!fHaveGrant)
     359           0 :             return;
     360           0 :         sem->post();
     361           0 :         fHaveGrant = false;
     362           0 :     }
     363             : 
     364           0 :     bool TryAcquire()
     365             :     {
     366           0 :         if (!fHaveGrant && sem->try_wait())
     367           0 :             fHaveGrant = true;
     368           0 :         return fHaveGrant;
     369             :     }
     370             : 
     371           0 :     void MoveTo(CSemaphoreGrant& grant)
     372             :     {
     373           0 :         grant.Release();
     374           0 :         grant.sem = sem;
     375           0 :         grant.fHaveGrant = fHaveGrant;
     376           0 :         fHaveGrant = false;
     377           0 :     }
     378             : 
     379           0 :     CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {}
     380             : 
     381           0 :     explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false)
     382             :     {
     383           0 :         if (fTry)
     384           0 :             TryAcquire();
     385             :         else
     386           0 :             Acquire();
     387           0 :     }
     388             : 
     389           0 :     ~CSemaphoreGrant()
     390             :     {
     391           0 :         Release();
     392           0 :     }
     393             : 
     394           0 :     operator bool() const
     395             :     {
     396           0 :         return fHaveGrant;
     397             :     }
     398             : };
     399             : 
     400             : #endif // BITCOIN_SYNC_H

Generated by: LCOV version 1.14