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