(root)/
gcc-13.2.0/
libsanitizer/
sanitizer_common/
sanitizer_mutex.h
       1  //===-- sanitizer_mutex.h ---------------------------------------*- C++ -*-===//
       2  //
       3  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
       4  // See https://llvm.org/LICENSE.txt for license information.
       5  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
       6  //
       7  //===----------------------------------------------------------------------===//
       8  //
       9  // This file is a part of ThreadSanitizer/AddressSanitizer runtime.
      10  //
      11  //===----------------------------------------------------------------------===//
      12  
      13  #ifndef SANITIZER_MUTEX_H
      14  #define SANITIZER_MUTEX_H
      15  
      16  #include "sanitizer_atomic.h"
      17  #include "sanitizer_internal_defs.h"
      18  #include "sanitizer_libc.h"
      19  #include "sanitizer_thread_safety.h"
      20  
      21  namespace __sanitizer {
      22  
      23  class SANITIZER_MUTEX StaticSpinMutex {
      24   public:
      25    void Init() {
      26      atomic_store(&state_, 0, memory_order_relaxed);
      27    }
      28  
      29    void Lock() SANITIZER_ACQUIRE() {
      30      if (LIKELY(TryLock()))
      31        return;
      32      LockSlow();
      33    }
      34  
      35    bool TryLock() SANITIZER_TRY_ACQUIRE(true) {
      36      return atomic_exchange(&state_, 1, memory_order_acquire) == 0;
      37    }
      38  
      39    void Unlock() SANITIZER_RELEASE() {
      40      atomic_store(&state_, 0, memory_order_release);
      41    }
      42  
      43    void CheckLocked() const SANITIZER_CHECK_LOCKED() {
      44      CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1);
      45    }
      46  
      47   private:
      48    atomic_uint8_t state_;
      49  
      50    void LockSlow();
      51  };
      52  
      53  class SANITIZER_MUTEX SpinMutex : public StaticSpinMutex {
      54   public:
      55    SpinMutex() {
      56      Init();
      57    }
      58  
      59    SpinMutex(const SpinMutex &) = delete;
      60    void operator=(const SpinMutex &) = delete;
      61  };
      62  
      63  // Semaphore provides an OS-dependent way to park/unpark threads.
      64  // The last thread returned from Wait can destroy the object
      65  // (destruction-safety).
      66  class Semaphore {
      67   public:
      68    constexpr Semaphore() {}
      69    Semaphore(const Semaphore &) = delete;
      70    void operator=(const Semaphore &) = delete;
      71  
      72    void Wait();
      73    void Post(u32 count = 1);
      74  
      75   private:
      76    atomic_uint32_t state_ = {0};
      77  };
      78  
      79  typedef int MutexType;
      80  
      81  enum {
      82    // Used as sentinel and to catch unassigned types
      83    // (should not be used as real Mutex type).
      84    MutexInvalid = 0,
      85    MutexThreadRegistry,
      86    // Each tool own mutexes must start at this number.
      87    MutexLastCommon,
      88    // Type for legacy mutexes that are not checked for deadlocks.
      89    MutexUnchecked = -1,
      90    // Special marks that can be used in MutexMeta::can_lock table.
      91    // The leaf mutexes can be locked under any other non-leaf mutex,
      92    // but no other mutex can be locked while under a leaf mutex.
      93    MutexLeaf = -1,
      94    // Multiple mutexes of this type can be locked at the same time.
      95    MutexMulti = -3,
      96  };
      97  
      98  // Go linker does not support THREADLOCAL variables,
      99  // so we can't use per-thread state.
     100  // Disable checked locks on Darwin. Although Darwin platforms support
     101  // THREADLOCAL variables they are not usable early on during process init when
     102  // `__sanitizer::Mutex` is used.
     103  #define SANITIZER_CHECK_DEADLOCKS \
     104    (SANITIZER_DEBUG && !SANITIZER_GO && SANITIZER_SUPPORTS_THREADLOCAL && !SANITIZER_APPLE)
     105  
     106  #if SANITIZER_CHECK_DEADLOCKS
     107  struct MutexMeta {
     108    MutexType type;
     109    const char *name;
     110    // The table fixes what mutexes can be locked under what mutexes.
     111    // If the entry for MutexTypeFoo contains MutexTypeBar,
     112    // then Bar mutex can be locked while under Foo mutex.
     113    // Can also contain the special MutexLeaf/MutexMulti marks.
     114    MutexType can_lock[10];
     115  };
     116  #endif
     117  
     118  class CheckedMutex {
     119   public:
     120    explicit constexpr CheckedMutex(MutexType type)
     121  #if SANITIZER_CHECK_DEADLOCKS
     122        : type_(type)
     123  #endif
     124    {
     125    }
     126  
     127    ALWAYS_INLINE void Lock() {
     128  #if SANITIZER_CHECK_DEADLOCKS
     129      LockImpl(GET_CALLER_PC());
     130  #endif
     131    }
     132  
     133    ALWAYS_INLINE void Unlock() {
     134  #if SANITIZER_CHECK_DEADLOCKS
     135      UnlockImpl();
     136  #endif
     137    }
     138  
     139    // Checks that the current thread does not hold any mutexes
     140    // (e.g. when returning from a runtime function to user code).
     141    static void CheckNoLocks() {
     142  #if SANITIZER_CHECK_DEADLOCKS
     143      CheckNoLocksImpl();
     144  #endif
     145    }
     146  
     147   private:
     148  #if SANITIZER_CHECK_DEADLOCKS
     149    const MutexType type_;
     150  
     151    void LockImpl(uptr pc);
     152    void UnlockImpl();
     153    static void CheckNoLocksImpl();
     154  #endif
     155  };
     156  
     157  // Reader-writer mutex.
     158  // Derive from CheckedMutex for the purposes of EBO.
     159  // We could make it a field marked with [[no_unique_address]],
     160  // but this attribute is not supported by some older compilers.
     161  class SANITIZER_MUTEX Mutex : CheckedMutex {
     162   public:
     163    explicit constexpr Mutex(MutexType type = MutexUnchecked)
     164        : CheckedMutex(type) {}
     165  
     166    void Lock() SANITIZER_ACQUIRE() {
     167      CheckedMutex::Lock();
     168      u64 reset_mask = ~0ull;
     169      u64 state = atomic_load_relaxed(&state_);
     170      for (uptr spin_iters = 0;; spin_iters++) {
     171        u64 new_state;
     172        bool locked = (state & (kWriterLock | kReaderLockMask)) != 0;
     173        if (LIKELY(!locked)) {
     174          // The mutex is not read-/write-locked, try to lock.
     175          new_state = (state | kWriterLock) & reset_mask;
     176        } else if (spin_iters > kMaxSpinIters) {
     177          // We've spun enough, increment waiting writers count and block.
     178          // The counter will be decremented by whoever wakes us.
     179          new_state = (state + kWaitingWriterInc) & reset_mask;
     180        } else if ((state & kWriterSpinWait) == 0) {
     181          // Active spinning, but denote our presence so that unlocking
     182          // thread does not wake up other threads.
     183          new_state = state | kWriterSpinWait;
     184        } else {
     185          // Active spinning.
     186          state = atomic_load(&state_, memory_order_relaxed);
     187          continue;
     188        }
     189        if (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state,
     190                                                   memory_order_acquire)))
     191          continue;
     192        if (LIKELY(!locked))
     193          return;  // We've locked the mutex.
     194        if (spin_iters > kMaxSpinIters) {
     195          // We've incremented waiting writers, so now block.
     196          writers_.Wait();
     197          spin_iters = 0;
     198        } else {
     199          // We've set kWriterSpinWait, but we are still in active spinning.
     200        }
     201        // We either blocked and were unblocked,
     202        // or we just spun but set kWriterSpinWait.
     203        // Either way we need to reset kWriterSpinWait
     204        // next time we take the lock or block again.
     205        reset_mask = ~kWriterSpinWait;
     206        state = atomic_load(&state_, memory_order_relaxed);
     207        DCHECK_NE(state & kWriterSpinWait, 0);
     208      }
     209    }
     210  
     211    bool TryLock() SANITIZER_TRY_ACQUIRE(true) {
     212      u64 state = atomic_load_relaxed(&state_);
     213      for (;;) {
     214        if (UNLIKELY(state & (kWriterLock | kReaderLockMask)))
     215          return false;
     216        // The mutex is not read-/write-locked, try to lock.
     217        if (LIKELY(atomic_compare_exchange_weak(
     218                &state_, &state, state | kWriterLock, memory_order_acquire))) {
     219          CheckedMutex::Lock();
     220          return true;
     221        }
     222      }
     223    }
     224  
     225    void Unlock() SANITIZER_RELEASE() {
     226      CheckedMutex::Unlock();
     227      bool wake_writer;
     228      u64 wake_readers;
     229      u64 new_state;
     230      u64 state = atomic_load_relaxed(&state_);
     231      do {
     232        DCHECK_NE(state & kWriterLock, 0);
     233        DCHECK_EQ(state & kReaderLockMask, 0);
     234        new_state = state & ~kWriterLock;
     235        wake_writer = (state & (kWriterSpinWait | kReaderSpinWait)) == 0 &&
     236                      (state & kWaitingWriterMask) != 0;
     237        if (wake_writer)
     238          new_state = (new_state - kWaitingWriterInc) | kWriterSpinWait;
     239        wake_readers =
     240            wake_writer || (state & kWriterSpinWait) != 0
     241                ? 0
     242                : ((state & kWaitingReaderMask) >> kWaitingReaderShift);
     243        if (wake_readers)
     244          new_state = (new_state & ~kWaitingReaderMask) | kReaderSpinWait;
     245      } while (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state,
     246                                                      memory_order_release)));
     247      if (UNLIKELY(wake_writer))
     248        writers_.Post();
     249      else if (UNLIKELY(wake_readers))
     250        readers_.Post(wake_readers);
     251    }
     252  
     253    void ReadLock() SANITIZER_ACQUIRE_SHARED() {
     254      CheckedMutex::Lock();
     255      u64 reset_mask = ~0ull;
     256      u64 state = atomic_load_relaxed(&state_);
     257      for (uptr spin_iters = 0;; spin_iters++) {
     258        bool locked = (state & kWriterLock) != 0;
     259        u64 new_state;
     260        if (LIKELY(!locked)) {
     261          new_state = (state + kReaderLockInc) & reset_mask;
     262        } else if (spin_iters > kMaxSpinIters) {
     263          new_state = (state + kWaitingReaderInc) & reset_mask;
     264        } else if ((state & kReaderSpinWait) == 0) {
     265          // Active spinning, but denote our presence so that unlocking
     266          // thread does not wake up other threads.
     267          new_state = state | kReaderSpinWait;
     268        } else {
     269          // Active spinning.
     270          state = atomic_load(&state_, memory_order_relaxed);
     271          continue;
     272        }
     273        if (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state,
     274                                                   memory_order_acquire)))
     275          continue;
     276        if (LIKELY(!locked))
     277          return;  // We've locked the mutex.
     278        if (spin_iters > kMaxSpinIters) {
     279          // We've incremented waiting readers, so now block.
     280          readers_.Wait();
     281          spin_iters = 0;
     282        } else {
     283          // We've set kReaderSpinWait, but we are still in active spinning.
     284        }
     285        reset_mask = ~kReaderSpinWait;
     286        state = atomic_load(&state_, memory_order_relaxed);
     287      }
     288    }
     289  
     290    void ReadUnlock() SANITIZER_RELEASE_SHARED() {
     291      CheckedMutex::Unlock();
     292      bool wake;
     293      u64 new_state;
     294      u64 state = atomic_load_relaxed(&state_);
     295      do {
     296        DCHECK_NE(state & kReaderLockMask, 0);
     297        DCHECK_EQ(state & kWriterLock, 0);
     298        new_state = state - kReaderLockInc;
     299        wake = (new_state &
     300                (kReaderLockMask | kWriterSpinWait | kReaderSpinWait)) == 0 &&
     301               (new_state & kWaitingWriterMask) != 0;
     302        if (wake)
     303          new_state = (new_state - kWaitingWriterInc) | kWriterSpinWait;
     304      } while (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state,
     305                                                      memory_order_release)));
     306      if (UNLIKELY(wake))
     307        writers_.Post();
     308    }
     309  
     310    // This function does not guarantee an explicit check that the calling thread
     311    // is the thread which owns the mutex. This behavior, while more strictly
     312    // correct, causes problems in cases like StopTheWorld, where a parent thread
     313    // owns the mutex but a child checks that it is locked. Rather than
     314    // maintaining complex state to work around those situations, the check only
     315    // checks that the mutex is owned.
     316    void CheckWriteLocked() const SANITIZER_CHECK_LOCKED() {
     317      CHECK(atomic_load(&state_, memory_order_relaxed) & kWriterLock);
     318    }
     319  
     320    void CheckLocked() const SANITIZER_CHECK_LOCKED() { CheckWriteLocked(); }
     321  
     322    void CheckReadLocked() const SANITIZER_CHECK_LOCKED() {
     323      CHECK(atomic_load(&state_, memory_order_relaxed) & kReaderLockMask);
     324    }
     325  
     326   private:
     327    atomic_uint64_t state_ = {0};
     328    Semaphore writers_;
     329    Semaphore readers_;
     330  
     331    // The state has 3 counters:
     332    //  - number of readers holding the lock,
     333    //    if non zero, the mutex is read-locked
     334    //  - number of waiting readers,
     335    //    if not zero, the mutex is write-locked
     336    //  - number of waiting writers,
     337    //    if non zero, the mutex is read- or write-locked
     338    // And 2 flags:
     339    //  - writer lock
     340    //    if set, the mutex is write-locked
     341    //  - a writer is awake and spin-waiting
     342    //    the flag is used to prevent thundering herd problem
     343    //    (new writers are not woken if this flag is set)
     344    //  - a reader is awake and spin-waiting
     345    //
     346    // Both writers and readers use active spinning before blocking.
     347    // But readers are more aggressive and always take the mutex
     348    // if there are any other readers.
     349    // After wake up both writers and readers compete to lock the
     350    // mutex again. This is needed to allow repeated locks even in presence
     351    // of other blocked threads.
     352    static constexpr u64 kCounterWidth = 20;
     353    static constexpr u64 kReaderLockShift = 0;
     354    static constexpr u64 kReaderLockInc = 1ull << kReaderLockShift;
     355    static constexpr u64 kReaderLockMask = ((1ull << kCounterWidth) - 1)
     356                                           << kReaderLockShift;
     357    static constexpr u64 kWaitingReaderShift = kCounterWidth;
     358    static constexpr u64 kWaitingReaderInc = 1ull << kWaitingReaderShift;
     359    static constexpr u64 kWaitingReaderMask = ((1ull << kCounterWidth) - 1)
     360                                              << kWaitingReaderShift;
     361    static constexpr u64 kWaitingWriterShift = 2 * kCounterWidth;
     362    static constexpr u64 kWaitingWriterInc = 1ull << kWaitingWriterShift;
     363    static constexpr u64 kWaitingWriterMask = ((1ull << kCounterWidth) - 1)
     364                                              << kWaitingWriterShift;
     365    static constexpr u64 kWriterLock = 1ull << (3 * kCounterWidth);
     366    static constexpr u64 kWriterSpinWait = 1ull << (3 * kCounterWidth + 1);
     367    static constexpr u64 kReaderSpinWait = 1ull << (3 * kCounterWidth + 2);
     368  
     369    static constexpr uptr kMaxSpinIters = 1500;
     370  
     371    Mutex(LinkerInitialized) = delete;
     372    Mutex(const Mutex &) = delete;
     373    void operator=(const Mutex &) = delete;
     374  };
     375  
     376  void FutexWait(atomic_uint32_t *p, u32 cmp);
     377  void FutexWake(atomic_uint32_t *p, u32 count);
     378  
     379  template <typename MutexType>
     380  class SANITIZER_SCOPED_LOCK GenericScopedLock {
     381   public:
     382    explicit GenericScopedLock(MutexType *mu) SANITIZER_ACQUIRE(mu) : mu_(mu) {
     383      mu_->Lock();
     384    }
     385  
     386    ~GenericScopedLock() SANITIZER_RELEASE() { mu_->Unlock(); }
     387  
     388   private:
     389    MutexType *mu_;
     390  
     391    GenericScopedLock(const GenericScopedLock &) = delete;
     392    void operator=(const GenericScopedLock &) = delete;
     393  };
     394  
     395  template <typename MutexType>
     396  class SANITIZER_SCOPED_LOCK GenericScopedReadLock {
     397   public:
     398    explicit GenericScopedReadLock(MutexType *mu) SANITIZER_ACQUIRE(mu)
     399        : mu_(mu) {
     400      mu_->ReadLock();
     401    }
     402  
     403    ~GenericScopedReadLock() SANITIZER_RELEASE() { mu_->ReadUnlock(); }
     404  
     405   private:
     406    MutexType *mu_;
     407  
     408    GenericScopedReadLock(const GenericScopedReadLock &) = delete;
     409    void operator=(const GenericScopedReadLock &) = delete;
     410  };
     411  
     412  template <typename MutexType>
     413  class SANITIZER_SCOPED_LOCK GenericScopedRWLock {
     414   public:
     415    ALWAYS_INLINE explicit GenericScopedRWLock(MutexType *mu, bool write)
     416        SANITIZER_ACQUIRE(mu)
     417        : mu_(mu), write_(write) {
     418      if (write_)
     419        mu_->Lock();
     420      else
     421        mu_->ReadLock();
     422    }
     423  
     424    ALWAYS_INLINE ~GenericScopedRWLock() SANITIZER_RELEASE() {
     425      if (write_)
     426        mu_->Unlock();
     427      else
     428        mu_->ReadUnlock();
     429    }
     430  
     431   private:
     432    MutexType *mu_;
     433    bool write_;
     434  
     435    GenericScopedRWLock(const GenericScopedRWLock &) = delete;
     436    void operator=(const GenericScopedRWLock &) = delete;
     437  };
     438  
     439  typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
     440  typedef GenericScopedLock<Mutex> Lock;
     441  typedef GenericScopedReadLock<Mutex> ReadLock;
     442  typedef GenericScopedRWLock<Mutex> RWLock;
     443  
     444  }  // namespace __sanitizer
     445  
     446  #endif  // SANITIZER_MUTEX_H