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 447