sync.h raw

   1  // Copyright (c) 2009-2010 Satoshi Nakamoto
   2  // Copyright (c) 2009-2022 The Limenka 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 LIMENKA_SYNC_H
   7  #define LIMENKA_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  inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false) {}
  76  inline void LeaveCritical() {}
  77  inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
  78  template <typename MutexType>
  79  inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs) {}
  80  template <typename MutexType>
  81  void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs) {}
  82  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  class LOCKABLE AnnotatedMixin : public PARENT
  92  {
  93  public:
  94      ~AnnotatedMixin() {
  95          DeleteLock((void*)this);
  96      }
  97  
  98      void lock() EXCLUSIVE_LOCK_FUNCTION()
  99      {
 100          PARENT::lock();
 101      }
 102  
 103      void unlock() UNLOCK_FUNCTION()
 104      {
 105          PARENT::unlock();
 106      }
 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/limenka/limenka/pull/20272#issuecomment-720755781
 139   */
 140  class GlobalMutex : public Mutex { };
 141  
 142  #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
 143  
 144  inline void AssertLockNotHeldInline(const char* name, const char* file, int line, Mutex* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) { AssertLockNotHeldInternal(name, file, line, cs); }
 145  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      void Enter(const char* pszName, const char* pszFile, int nLine)
 157      {
 158          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          Base::lock();
 164      }
 165  
 166      bool TryEnter(const char* pszName, const char* pszFile, int nLine)
 167      {
 168          EnterCritical(pszName, pszFile, nLine, Base::mutex(), true);
 169          if (Base::try_lock()) {
 170              return true;
 171          }
 172          LeaveCritical();
 173          return false;
 174      }
 175  
 176  public:
 177      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          if (fTry)
 180              TryEnter(pszName, pszFile, nLine);
 181          else
 182              Enter(pszName, pszFile, nLine);
 183      }
 184  
 185      UniqueLock(MutexType* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
 186      {
 187          if (!pmutexIn) return;
 188  
 189          *static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock);
 190          if (fTry)
 191              TryEnter(pszName, pszFile, nLine);
 192          else
 193              Enter(pszName, pszFile, nLine);
 194      }
 195  
 196      ~UniqueLock() UNLOCK_FUNCTION()
 197      {
 198          if (Base::owns_lock())
 199              LeaveCritical();
 200      }
 201  
 202      operator bool()
 203      {
 204          return Base::owns_lock();
 205      }
 206  
 207  protected:
 208      // needed for reverse_lock
 209      UniqueLock() = default;
 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          explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
 218              CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
 219              lock.unlock();
 220              LeaveCritical();
 221              lock.swap(templock);
 222          }
 223  
 224          ~reverse_lock() {
 225              templock.swap(lock);
 226              EnterCritical(lockname.c_str(), file.c_str(), line, lock.mutex());
 227              lock.lock();
 228          }
 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  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  inline MutexType& MaybeCheckNotHeld(MutexType& m) LOCKS_EXCLUDED(m) LOCK_RETURNED(m) { return m; }
 254  template <typename MutexType>
 255  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  class CSemaphore
 308  {
 309  private:
 310      std::condition_variable condition;
 311      std::mutex mutex;
 312      int value;
 313  
 314  public:
 315      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      void wait() noexcept
 325      {
 326          std::unique_lock<std::mutex> lock(mutex);
 327          condition.wait(lock, [&]() { return value >= 1; });
 328          value--;
 329      }
 330  
 331      bool try_wait() noexcept
 332      {
 333          std::lock_guard<std::mutex> lock(mutex);
 334          if (value < 1) {
 335              return false;
 336          }
 337          value--;
 338          return true;
 339      }
 340  
 341      void post() noexcept
 342      {
 343          {
 344              std::lock_guard<std::mutex> lock(mutex);
 345              value++;
 346          }
 347          condition.notify_one();
 348      }
 349  };
 350  
 351  /** RAII-style semaphore lock */
 352  class CSemaphoreGrant
 353  {
 354  private:
 355      CSemaphore* sem;
 356      bool fHaveGrant;
 357  
 358  public:
 359      void Acquire() noexcept
 360      {
 361          if (fHaveGrant) {
 362              return;
 363          }
 364          sem->wait();
 365          fHaveGrant = true;
 366      }
 367  
 368      void Release() noexcept
 369      {
 370          if (!fHaveGrant) {
 371              return;
 372          }
 373          sem->post();
 374          fHaveGrant = false;
 375      }
 376  
 377      bool TryAcquire() noexcept
 378      {
 379          if (!fHaveGrant && sem->try_wait()) {
 380              fHaveGrant = true;
 381          }
 382          return fHaveGrant;
 383      }
 384  
 385      // Disallow copy.
 386      CSemaphoreGrant(const CSemaphoreGrant&) = delete;
 387      CSemaphoreGrant& operator=(const CSemaphoreGrant&) = delete;
 388  
 389      // Allow move.
 390      CSemaphoreGrant(CSemaphoreGrant&& other) noexcept
 391      {
 392          sem = other.sem;
 393          fHaveGrant = other.fHaveGrant;
 394          other.fHaveGrant = false;
 395          other.sem = nullptr;
 396      }
 397  
 398      CSemaphoreGrant& operator=(CSemaphoreGrant&& other) noexcept
 399      {
 400          Release();
 401          sem = other.sem;
 402          fHaveGrant = other.fHaveGrant;
 403          other.fHaveGrant = false;
 404          other.sem = nullptr;
 405          return *this;
 406      }
 407  
 408      CSemaphoreGrant() noexcept : sem(nullptr), fHaveGrant(false) {}
 409  
 410      explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) noexcept : sem(&sema), fHaveGrant(false)
 411      {
 412          if (fTry) {
 413              TryAcquire();
 414          } else {
 415              Acquire();
 416          }
 417      }
 418  
 419      ~CSemaphoreGrant()
 420      {
 421          Release();
 422      }
 423  
 424      explicit operator bool() const noexcept
 425      {
 426          return fHaveGrant;
 427      }
 428  };
 429  
 430  #endif // LIMENKA_SYNC_H
 431