Home | History | Annotate | Line # | Download | only in sanitizer_common
sanitizer_mutex.h revision 1.1.1.8
      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 
     20 namespace __sanitizer {
     21 
     22 class StaticSpinMutex {
     23  public:
     24   void Init() {
     25     atomic_store(&state_, 0, memory_order_relaxed);
     26   }
     27 
     28   void Lock() {
     29     if (TryLock())
     30       return;
     31     LockSlow();
     32   }
     33 
     34   bool TryLock() {
     35     return atomic_exchange(&state_, 1, memory_order_acquire) == 0;
     36   }
     37 
     38   void Unlock() {
     39     atomic_store(&state_, 0, memory_order_release);
     40   }
     41 
     42   void CheckLocked() {
     43     CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1);
     44   }
     45 
     46  private:
     47   atomic_uint8_t state_;
     48 
     49   void NOINLINE LockSlow() {
     50     for (int i = 0;; i++) {
     51       if (i < 10)
     52         proc_yield(10);
     53       else
     54         internal_sched_yield();
     55       if (atomic_load(&state_, memory_order_relaxed) == 0
     56           && atomic_exchange(&state_, 1, memory_order_acquire) == 0)
     57         return;
     58     }
     59   }
     60 };
     61 
     62 class SpinMutex : public StaticSpinMutex {
     63  public:
     64   SpinMutex() {
     65     Init();
     66   }
     67 
     68  private:
     69   SpinMutex(const SpinMutex&);
     70   void operator=(const SpinMutex&);
     71 };
     72 
     73 class BlockingMutex {
     74  public:
     75   explicit constexpr BlockingMutex(LinkerInitialized)
     76       : opaque_storage_ {0, }, owner_ {0} {}
     77   BlockingMutex();
     78   void Lock();
     79   void Unlock();
     80 
     81   // This function does not guarantee an explicit check that the calling thread
     82   // is the thread which owns the mutex. This behavior, while more strictly
     83   // correct, causes problems in cases like StopTheWorld, where a parent thread
     84   // owns the mutex but a child checks that it is locked. Rather than
     85   // maintaining complex state to work around those situations, the check only
     86   // checks that the mutex is owned, and assumes callers to be generally
     87   // well-behaved.
     88   void CheckLocked();
     89 
     90  private:
     91   // Solaris mutex_t has a member that requires 64-bit alignment.
     92   ALIGNED(8) uptr opaque_storage_[10];
     93   uptr owner_;  // for debugging
     94 };
     95 
     96 // Reader-writer spin mutex.
     97 class RWMutex {
     98  public:
     99   RWMutex() {
    100     atomic_store(&state_, kUnlocked, memory_order_relaxed);
    101   }
    102 
    103   ~RWMutex() {
    104     CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);
    105   }
    106 
    107   void Lock() {
    108     u32 cmp = kUnlocked;
    109     if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
    110                                        memory_order_acquire))
    111       return;
    112     LockSlow();
    113   }
    114 
    115   void Unlock() {
    116     u32 prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
    117     DCHECK_NE(prev & kWriteLock, 0);
    118     (void)prev;
    119   }
    120 
    121   void ReadLock() {
    122     u32 prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
    123     if ((prev & kWriteLock) == 0)
    124       return;
    125     ReadLockSlow();
    126   }
    127 
    128   void ReadUnlock() {
    129     u32 prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);
    130     DCHECK_EQ(prev & kWriteLock, 0);
    131     DCHECK_GT(prev & ~kWriteLock, 0);
    132     (void)prev;
    133   }
    134 
    135   void CheckLocked() {
    136     CHECK_NE(atomic_load(&state_, memory_order_relaxed), kUnlocked);
    137   }
    138 
    139  private:
    140   atomic_uint32_t state_;
    141 
    142   enum {
    143     kUnlocked = 0,
    144     kWriteLock = 1,
    145     kReadLock = 2
    146   };
    147 
    148   void NOINLINE LockSlow() {
    149     for (int i = 0;; i++) {
    150       if (i < 10)
    151         proc_yield(10);
    152       else
    153         internal_sched_yield();
    154       u32 cmp = atomic_load(&state_, memory_order_relaxed);
    155       if (cmp == kUnlocked &&
    156           atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,
    157                                        memory_order_acquire))
    158           return;
    159     }
    160   }
    161 
    162   void NOINLINE ReadLockSlow() {
    163     for (int i = 0;; i++) {
    164       if (i < 10)
    165         proc_yield(10);
    166       else
    167         internal_sched_yield();
    168       u32 prev = atomic_load(&state_, memory_order_acquire);
    169       if ((prev & kWriteLock) == 0)
    170         return;
    171     }
    172   }
    173 
    174   RWMutex(const RWMutex&);
    175   void operator = (const RWMutex&);
    176 };
    177 
    178 template<typename MutexType>
    179 class GenericScopedLock {
    180  public:
    181   explicit GenericScopedLock(MutexType *mu)
    182       : mu_(mu) {
    183     mu_->Lock();
    184   }
    185 
    186   ~GenericScopedLock() {
    187     mu_->Unlock();
    188   }
    189 
    190  private:
    191   MutexType *mu_;
    192 
    193   GenericScopedLock(const GenericScopedLock&);
    194   void operator=(const GenericScopedLock&);
    195 };
    196 
    197 template<typename MutexType>
    198 class GenericScopedReadLock {
    199  public:
    200   explicit GenericScopedReadLock(MutexType *mu)
    201       : mu_(mu) {
    202     mu_->ReadLock();
    203   }
    204 
    205   ~GenericScopedReadLock() {
    206     mu_->ReadUnlock();
    207   }
    208 
    209  private:
    210   MutexType *mu_;
    211 
    212   GenericScopedReadLock(const GenericScopedReadLock&);
    213   void operator=(const GenericScopedReadLock&);
    214 };
    215 
    216 typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
    217 typedef GenericScopedLock<BlockingMutex> BlockingMutexLock;
    218 typedef GenericScopedLock<RWMutex> RWMutexLock;
    219 typedef GenericScopedReadLock<RWMutex> RWMutexReadLock;
    220 
    221 }  // namespace __sanitizer
    222 
    223 #endif  // SANITIZER_MUTEX_H
    224