LCOV - code coverage report
Current view: top level - src - sync.h (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 20 125 16.0 %
Date: 2024-01-03 14:57:27 Functions: 17 62 27.4 %
Branches: 4 68 5.9 %

           Branch data     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                 :            : template <typename MutexType>
      61                 :            : void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs);
      62                 :            : template <typename MutexType>
      63                 :            : void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs);
      64                 :            : void DeleteLock(void* cs);
      65                 :            : bool LockStackEmpty();
      66                 :            : 
      67                 :            : /**
      68                 :            :  * Call abort() if a potential lock order deadlock bug is detected, instead of
      69                 :            :  * just logging information and throwing a logic_error. Defaults to true, and
      70                 :            :  * set to false in DEBUG_LOCKORDER unit tests.
      71                 :            :  */
      72                 :            : extern bool g_debug_lockorder_abort;
      73                 :            : #else
      74                 :            : template <typename MutexType>
      75                 :         10 : inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false) {}
      76                 :         10 : inline void LeaveCritical() {}
      77                 :          0 : inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
      78                 :            : template <typename MutexType>
      79                 :          0 : inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs) {}
      80                 :            : template <typename MutexType>
      81                 :          0 : void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs) {}
      82                 :         46 : inline void DeleteLock(void* cs) {}
      83                 :            : inline bool LockStackEmpty() { return true; }
      84                 :            : #endif
      85                 :            : 
      86                 :            : /**
      87                 :            :  * Template mixin that adds -Wthread-safety locking annotations and lock order
      88                 :            :  * checking to a subset of the mutex API.
      89                 :            :  */
      90                 :            : template <typename PARENT>
      91                 :          0 : class LOCKABLE AnnotatedMixin : public PARENT
      92                 :            : {
      93                 :            : public:
      94                 :         46 :     ~AnnotatedMixin() {
      95         [ +  - ]:         46 :         DeleteLock((void*)this);
      96                 :         46 :     }
      97                 :            : 
      98                 :          0 :     void lock() EXCLUSIVE_LOCK_FUNCTION()
      99                 :            :     {
     100                 :          0 :         PARENT::lock();
     101                 :          0 :     }
     102                 :            : 
     103                 :          0 :     void unlock() UNLOCK_FUNCTION()
     104                 :            :     {
     105                 :          0 :         PARENT::unlock();
     106                 :          0 :     }
     107                 :            : 
     108                 :            :     bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true)
     109                 :            :     {
     110                 :            :         return PARENT::try_lock();
     111                 :            :     }
     112                 :            : 
     113                 :            :     using unique_lock = std::unique_lock<PARENT>;
     114                 :            : #ifdef __clang__
     115                 :            :     //! For negative capabilities in the Clang Thread Safety Analysis.
     116                 :            :     //! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in conjunction
     117                 :            :     //! with the ! operator, to indicate that a mutex should not be held.
     118                 :            :     const AnnotatedMixin& operator!() const { return *this; }
     119                 :            : #endif // __clang__
     120                 :            : };
     121                 :            : 
     122                 :            : /**
     123                 :            :  * Wrapped mutex: supports recursive locking, but no waiting
     124                 :            :  * TODO: We should move away from using the recursive lock by default.
     125                 :            :  */
     126                 :            : using RecursiveMutex = AnnotatedMixin<std::recursive_mutex>;
     127                 :            : 
     128                 :            : /** Wrapped mutex: supports waiting but not recursive locking */
     129                 :            : using Mutex = AnnotatedMixin<std::mutex>;
     130                 :            : 
     131                 :            : /** Different type to mark Mutex at global scope
     132                 :            :  *
     133                 :            :  * Thread safety analysis can't handle negative assertions about mutexes
     134                 :            :  * with global scope well, so mark them with a separate type, and
     135                 :            :  * eventually move all the mutexes into classes so they are not globally
     136                 :            :  * visible.
     137                 :            :  *
     138                 :            :  * See: https://github.com/bitcoin/bitcoin/pull/20272#issuecomment-720755781
     139                 :            :  */
     140                 :          0 : class GlobalMutex : public Mutex { };
     141                 :            : 
     142                 :            : #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
     143                 :            : 
     144                 :          0 : inline void AssertLockNotHeldInline(const char* name, const char* file, int line, Mutex* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) { AssertLockNotHeldInternal(name, file, line, cs); }
     145                 :          0 : inline void AssertLockNotHeldInline(const char* name, const char* file, int line, RecursiveMutex* cs) LOCKS_EXCLUDED(cs) { AssertLockNotHeldInternal(name, file, line, cs); }
     146                 :            : inline void AssertLockNotHeldInline(const char* name, const char* file, int line, GlobalMutex* cs) LOCKS_EXCLUDED(cs) { AssertLockNotHeldInternal(name, file, line, cs); }
     147                 :            : #define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs)
     148                 :            : 
     149                 :            : /** Wrapper around std::unique_lock style lock for MutexType. */
     150                 :            : template <typename MutexType>
     151                 :            : class SCOPED_LOCKABLE UniqueLock : public MutexType::unique_lock
     152                 :            : {
     153                 :            : private:
     154                 :            :     using Base = typename MutexType::unique_lock;
     155                 :            : 
     156                 :         10 :     void Enter(const char* pszName, const char* pszFile, int nLine)
     157                 :            :     {
     158                 :         10 :         EnterCritical(pszName, pszFile, nLine, Base::mutex());
     159                 :            : #ifdef DEBUG_LOCKCONTENTION
     160                 :            :         if (Base::try_lock()) return;
     161                 :            :         LOG_TIME_MICROS_WITH_CATEGORY(strprintf("lock contention %s, %s:%d", pszName, pszFile, nLine), BCLog::LOCK);
     162                 :            : #endif
     163                 :         10 :         Base::lock();
     164                 :         10 :     }
     165                 :            : 
     166                 :          0 :     bool TryEnter(const char* pszName, const char* pszFile, int nLine)
     167                 :            :     {
     168                 :          0 :         EnterCritical(pszName, pszFile, nLine, Base::mutex(), true);
     169 [ #  # ][ #  # ]:          0 :         if (Base::try_lock()) {
                 [ #  # ]
     170                 :          0 :             return true;
     171                 :            :         }
     172                 :          0 :         LeaveCritical();
     173                 :          0 :         return false;
     174                 :          0 :     }
     175                 :            : 
     176                 :            : public:
     177                 :         10 :     UniqueLock(MutexType& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
     178                 :            :     {
     179 [ -  + ][ #  # ]:         10 :         if (fTry)
                 [ #  # ]
     180 [ #  # ][ #  # ]:          0 :             TryEnter(pszName, pszFile, nLine);
                 [ #  # ]
     181                 :            :         else
     182 [ +  - ][ #  # ]:         10 :             Enter(pszName, pszFile, nLine);
                 [ #  # ]
     183                 :         10 :     }
     184                 :            : 
     185                 :          0 :     UniqueLock(MutexType* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
     186                 :            :     {
     187         [ #  # ]:          0 :         if (!pmutexIn) return;
     188                 :            : 
     189                 :          0 :         *static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock);
     190         [ #  # ]:          0 :         if (fTry)
     191         [ #  # ]:          0 :             TryEnter(pszName, pszFile, nLine);
     192                 :            :         else
     193         [ #  # ]:          0 :             Enter(pszName, pszFile, nLine);
     194                 :          0 :     }
     195                 :            : 
     196                 :         10 :     ~UniqueLock() UNLOCK_FUNCTION()
     197                 :            :     {
     198 [ -  + ][ #  # ]:         10 :         if (Base::owns_lock())
                 [ #  # ]
     199                 :         10 :             LeaveCritical();
     200                 :         10 :     }
     201                 :            : 
     202                 :          0 :     operator bool()
     203                 :            :     {
     204                 :          0 :         return Base::owns_lock();
     205                 :            :     }
     206                 :            : 
     207                 :            : protected:
     208                 :            :     // needed for reverse_lock
     209                 :          0 :     UniqueLock() { }
     210                 :            : 
     211                 :            : public:
     212                 :            :     /**
     213                 :            :      * An RAII-style reverse lock. Unlocks on construction and locks on destruction.
     214                 :            :      */
     215                 :            :     class reverse_lock {
     216                 :            :     public:
     217         [ #  # ]:          0 :         explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
     218         [ #  # ]:          0 :             CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
     219         [ #  # ]:          0 :             lock.unlock();
     220                 :          0 :             LeaveCritical();
     221                 :          0 :             lock.swap(templock);
     222                 :          0 :         }
     223                 :            : 
     224                 :          0 :         ~reverse_lock() {
     225                 :          0 :             templock.swap(lock);
     226                 :          0 :             EnterCritical(lockname.c_str(), file.c_str(), line, lock.mutex());
     227         [ #  # ]:          0 :             lock.lock();
     228                 :          0 :         }
     229                 :            : 
     230                 :            :      private:
     231                 :            :         reverse_lock(reverse_lock const&);
     232                 :            :         reverse_lock& operator=(reverse_lock const&);
     233                 :            : 
     234                 :            :         UniqueLock& lock;
     235                 :            :         UniqueLock templock;
     236                 :            :         std::string lockname;
     237                 :            :         const std::string file;
     238                 :            :         const int line;
     239                 :            :      };
     240                 :            :      friend class reverse_lock;
     241                 :            : };
     242                 :            : 
     243                 :            : #define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, #g, __FILE__, __LINE__)
     244                 :            : 
     245                 :            : // When locking a Mutex, require negative capability to ensure the lock
     246                 :            : // is not already held
     247                 :          9 : inline Mutex& MaybeCheckNotHeld(Mutex& cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) LOCK_RETURNED(cs) { return cs; }
     248                 :            : inline Mutex* MaybeCheckNotHeld(Mutex* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) LOCK_RETURNED(cs) { return cs; }
     249                 :            : 
     250                 :            : // When locking a GlobalMutex or RecursiveMutex, just check it is not
     251                 :            : // locked in the surrounding scope.
     252                 :            : template <typename MutexType>
     253                 :          1 : inline MutexType& MaybeCheckNotHeld(MutexType& m) LOCKS_EXCLUDED(m) LOCK_RETURNED(m) { return m; }
     254                 :            : template <typename MutexType>
     255                 :          0 : inline MutexType* MaybeCheckNotHeld(MutexType* m) LOCKS_EXCLUDED(m) LOCK_RETURNED(m) { return m; }
     256                 :            : 
     257                 :            : #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
     258                 :            : #define LOCK2(cs1, cs2)                                               \
     259                 :            :     UniqueLock criticalblock1(MaybeCheckNotHeld(cs1), #cs1, __FILE__, __LINE__); \
     260                 :            :     UniqueLock criticalblock2(MaybeCheckNotHeld(cs2), #cs2, __FILE__, __LINE__)
     261                 :            : #define TRY_LOCK(cs, name) UniqueLock name(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__, true)
     262                 :            : #define WAIT_LOCK(cs, name) UniqueLock name(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
     263                 :            : 
     264                 :            : #define ENTER_CRITICAL_SECTION(cs)                            \
     265                 :            :     {                                                         \
     266                 :            :         EnterCritical(#cs, __FILE__, __LINE__, &cs); \
     267                 :            :         (cs).lock();                                          \
     268                 :            :     }
     269                 :            : 
     270                 :            : #define LEAVE_CRITICAL_SECTION(cs)                                          \
     271                 :            :     {                                                                       \
     272                 :            :         std::string lockname;                                               \
     273                 :            :         CheckLastCritical((void*)(&cs), lockname, #cs, __FILE__, __LINE__); \
     274                 :            :         (cs).unlock();                                                      \
     275                 :            :         LeaveCritical();                                                    \
     276                 :            :     }
     277                 :            : 
     278                 :            : //! Run code while locking a mutex.
     279                 :            : //!
     280                 :            : //! Examples:
     281                 :            : //!
     282                 :            : //!   WITH_LOCK(cs, shared_val = shared_val + 1);
     283                 :            : //!
     284                 :            : //!   int val = WITH_LOCK(cs, return shared_val);
     285                 :            : //!
     286                 :            : //! Note:
     287                 :            : //!
     288                 :            : //! Since the return type deduction follows that of decltype(auto), while the
     289                 :            : //! deduced type of:
     290                 :            : //!
     291                 :            : //!   WITH_LOCK(cs, return {int i = 1; return i;});
     292                 :            : //!
     293                 :            : //! is int, the deduced type of:
     294                 :            : //!
     295                 :            : //!   WITH_LOCK(cs, return {int j = 1; return (j);});
     296                 :            : //!
     297                 :            : //! is &int, a reference to a local variable
     298                 :            : //!
     299                 :            : //! The above is detectable at compile-time with the -Wreturn-local-addr flag in
     300                 :            : //! gcc and the -Wreturn-stack-address flag in clang, both enabled by default.
     301                 :            : #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }())
     302                 :            : 
     303                 :            : /** An implementation of a semaphore.
     304                 :            :  *
     305                 :            :  * See https://en.wikipedia.org/wiki/Semaphore_(programming)
     306                 :            :  */
     307                 :          0 : class CSemaphore
     308                 :            : {
     309                 :            : private:
     310                 :            :     std::condition_variable condition;
     311                 :            :     std::mutex mutex;
     312                 :            :     int value;
     313                 :            : 
     314                 :            : public:
     315                 :          0 :     explicit CSemaphore(int init) noexcept : value(init) {}
     316                 :            : 
     317                 :            :     // Disallow default construct, copy, move.
     318                 :            :     CSemaphore() = delete;
     319                 :            :     CSemaphore(const CSemaphore&) = delete;
     320                 :            :     CSemaphore(CSemaphore&&) = delete;
     321                 :            :     CSemaphore& operator=(const CSemaphore&) = delete;
     322                 :            :     CSemaphore& operator=(CSemaphore&&) = delete;
     323                 :            : 
     324                 :          0 :     void wait() noexcept
     325                 :            :     {
     326         [ #  # ]:          0 :         std::unique_lock<std::mutex> lock(mutex);
     327         [ #  # ]:          0 :         condition.wait(lock, [&]() { return value >= 1; });
     328                 :          0 :         value--;
     329                 :          0 :     }
     330                 :            : 
     331                 :          0 :     bool try_wait() noexcept
     332                 :            :     {
     333         [ #  # ]:          0 :         std::lock_guard<std::mutex> lock(mutex);
     334         [ #  # ]:          0 :         if (value < 1) {
     335                 :          0 :             return false;
     336                 :            :         }
     337                 :          0 :         value--;
     338                 :          0 :         return true;
     339                 :          0 :     }
     340                 :            : 
     341                 :          0 :     void post() noexcept
     342                 :            :     {
     343                 :            :         {
     344         [ #  # ]:          0 :             std::lock_guard<std::mutex> lock(mutex);
     345                 :          0 :             value++;
     346                 :          0 :         }
     347                 :          0 :         condition.notify_one();
     348                 :          0 :     }
     349                 :            : };
     350                 :            : 
     351                 :            : /** RAII-style semaphore lock */
     352                 :            : class CSemaphoreGrant
     353                 :            : {
     354                 :            : private:
     355                 :            :     CSemaphore* sem;
     356                 :            :     bool fHaveGrant;
     357                 :            : 
     358                 :            : public:
     359                 :          0 :     void Acquire() noexcept
     360                 :            :     {
     361         [ #  # ]:          0 :         if (fHaveGrant) {
     362                 :          0 :             return;
     363                 :            :         }
     364                 :          0 :         sem->wait();
     365                 :          0 :         fHaveGrant = true;
     366                 :          0 :     }
     367                 :            : 
     368                 :          0 :     void Release() noexcept
     369                 :            :     {
     370         [ #  # ]:          0 :         if (!fHaveGrant) {
     371                 :          0 :             return;
     372                 :            :         }
     373                 :          0 :         sem->post();
     374                 :          0 :         fHaveGrant = false;
     375                 :          0 :     }
     376                 :            : 
     377                 :          0 :     bool TryAcquire() noexcept
     378                 :            :     {
     379 [ #  # ][ #  # ]:          0 :         if (!fHaveGrant && sem->try_wait()) {
     380                 :          0 :             fHaveGrant = true;
     381                 :          0 :         }
     382                 :          0 :         return fHaveGrant;
     383                 :            :     }
     384                 :            : 
     385                 :            :     // Disallow copy.
     386                 :            :     CSemaphoreGrant(const CSemaphoreGrant&) = delete;
     387                 :            :     CSemaphoreGrant& operator=(const CSemaphoreGrant&) = delete;
     388                 :            : 
     389                 :            :     // Allow move.
     390                 :          0 :     CSemaphoreGrant(CSemaphoreGrant&& other) noexcept
     391                 :            :     {
     392                 :          0 :         sem = other.sem;
     393                 :          0 :         fHaveGrant = other.fHaveGrant;
     394                 :          0 :         other.fHaveGrant = false;
     395                 :          0 :         other.sem = nullptr;
     396                 :          0 :     }
     397                 :            : 
     398                 :          0 :     CSemaphoreGrant& operator=(CSemaphoreGrant&& other) noexcept
     399                 :            :     {
     400                 :          0 :         Release();
     401                 :          0 :         sem = other.sem;
     402                 :          0 :         fHaveGrant = other.fHaveGrant;
     403                 :          0 :         other.fHaveGrant = false;
     404                 :          0 :         other.sem = nullptr;
     405                 :          0 :         return *this;
     406                 :            :     }
     407                 :            : 
     408                 :          0 :     CSemaphoreGrant() noexcept : sem(nullptr), fHaveGrant(false) {}
     409                 :            : 
     410                 :          0 :     explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) noexcept : sem(&sema), fHaveGrant(false)
     411                 :            :     {
     412         [ #  # ]:          0 :         if (fTry) {
     413                 :          0 :             TryAcquire();
     414                 :          0 :         } else {
     415                 :          0 :             Acquire();
     416                 :            :         }
     417                 :          0 :     }
     418                 :            : 
     419                 :          0 :     ~CSemaphoreGrant()
     420                 :            :     {
     421                 :          0 :         Release();
     422                 :          0 :     }
     423                 :            : 
     424                 :          0 :     explicit operator bool() const noexcept
     425                 :            :     {
     426                 :          0 :         return fHaveGrant;
     427                 :            :     }
     428                 :            : };
     429                 :            : 
     430                 :            : #endif // BITCOIN_SYNC_H

Generated by: LCOV version 1.14