10b57cec5SDimitry Andric //===-- sanitizer_mutex.h ---------------------------------------*- C++ -*-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file is a part of ThreadSanitizer/AddressSanitizer runtime. 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 130b57cec5SDimitry Andric #ifndef SANITIZER_MUTEX_H 140b57cec5SDimitry Andric #define SANITIZER_MUTEX_H 150b57cec5SDimitry Andric 160b57cec5SDimitry Andric #include "sanitizer_atomic.h" 170b57cec5SDimitry Andric #include "sanitizer_internal_defs.h" 180b57cec5SDimitry Andric #include "sanitizer_libc.h" 19fe6060f1SDimitry Andric #include "sanitizer_thread_safety.h" 200b57cec5SDimitry Andric 210b57cec5SDimitry Andric namespace __sanitizer { 220b57cec5SDimitry Andric 2304eeddc0SDimitry Andric class SANITIZER_MUTEX StaticSpinMutex { 240b57cec5SDimitry Andric public: Init()250b57cec5SDimitry Andric void Init() { 260b57cec5SDimitry Andric atomic_store(&state_, 0, memory_order_relaxed); 270b57cec5SDimitry Andric } 280b57cec5SDimitry Andric Lock()2904eeddc0SDimitry Andric void Lock() SANITIZER_ACQUIRE() { 30fe6060f1SDimitry Andric if (LIKELY(TryLock())) 310b57cec5SDimitry Andric return; 320b57cec5SDimitry Andric LockSlow(); 330b57cec5SDimitry Andric } 340b57cec5SDimitry Andric TryLock()3504eeddc0SDimitry Andric bool TryLock() SANITIZER_TRY_ACQUIRE(true) { 360b57cec5SDimitry Andric return atomic_exchange(&state_, 1, memory_order_acquire) == 0; 370b57cec5SDimitry Andric } 380b57cec5SDimitry Andric Unlock()3904eeddc0SDimitry Andric void Unlock() SANITIZER_RELEASE() { 4004eeddc0SDimitry Andric atomic_store(&state_, 0, memory_order_release); 4104eeddc0SDimitry Andric } 420b57cec5SDimitry Andric CheckLocked()4304eeddc0SDimitry Andric void CheckLocked() const SANITIZER_CHECK_LOCKED() { 440b57cec5SDimitry Andric CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1); 450b57cec5SDimitry Andric } 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric private: 480b57cec5SDimitry Andric atomic_uint8_t state_; 490b57cec5SDimitry Andric 50fe6060f1SDimitry Andric void LockSlow(); 510b57cec5SDimitry Andric }; 520b57cec5SDimitry Andric 5304eeddc0SDimitry Andric class SANITIZER_MUTEX SpinMutex : public StaticSpinMutex { 540b57cec5SDimitry Andric public: SpinMutex()550b57cec5SDimitry Andric SpinMutex() { 560b57cec5SDimitry Andric Init(); 570b57cec5SDimitry Andric } 580b57cec5SDimitry Andric 59fe6060f1SDimitry Andric SpinMutex(const SpinMutex &) = delete; 60fe6060f1SDimitry Andric void operator=(const SpinMutex &) = delete; 610b57cec5SDimitry Andric }; 620b57cec5SDimitry Andric 63fe6060f1SDimitry Andric // Semaphore provides an OS-dependent way to park/unpark threads. 64fe6060f1SDimitry Andric // The last thread returned from Wait can destroy the object 65fe6060f1SDimitry Andric // (destruction-safety). 66fe6060f1SDimitry Andric class Semaphore { 67fe6060f1SDimitry Andric public: Semaphore()68fe6060f1SDimitry Andric constexpr Semaphore() {} 69fe6060f1SDimitry Andric Semaphore(const Semaphore &) = delete; 70fe6060f1SDimitry Andric void operator=(const Semaphore &) = delete; 71fe6060f1SDimitry Andric 72fe6060f1SDimitry Andric void Wait(); 73fe6060f1SDimitry Andric void Post(u32 count = 1); 74fe6060f1SDimitry Andric 75fe6060f1SDimitry Andric private: 76fe6060f1SDimitry Andric atomic_uint32_t state_ = {0}; 77fe6060f1SDimitry Andric }; 78fe6060f1SDimitry Andric 79fe6060f1SDimitry Andric typedef int MutexType; 80fe6060f1SDimitry Andric 81fe6060f1SDimitry Andric enum { 82fe6060f1SDimitry Andric // Used as sentinel and to catch unassigned types 83fe6060f1SDimitry Andric // (should not be used as real Mutex type). 84fe6060f1SDimitry Andric MutexInvalid = 0, 85fe6060f1SDimitry Andric MutexThreadRegistry, 86fe6060f1SDimitry Andric // Each tool own mutexes must start at this number. 87fe6060f1SDimitry Andric MutexLastCommon, 88fe6060f1SDimitry Andric // Type for legacy mutexes that are not checked for deadlocks. 89fe6060f1SDimitry Andric MutexUnchecked = -1, 90fe6060f1SDimitry Andric // Special marks that can be used in MutexMeta::can_lock table. 91fe6060f1SDimitry Andric // The leaf mutexes can be locked under any other non-leaf mutex, 92fe6060f1SDimitry Andric // but no other mutex can be locked while under a leaf mutex. 93fe6060f1SDimitry Andric MutexLeaf = -1, 94fe6060f1SDimitry Andric // Multiple mutexes of this type can be locked at the same time. 95fe6060f1SDimitry Andric MutexMulti = -3, 96fe6060f1SDimitry Andric }; 97fe6060f1SDimitry Andric 98fe6060f1SDimitry Andric // Go linker does not support THREADLOCAL variables, 99fe6060f1SDimitry Andric // so we can't use per-thread state. 100349cc55cSDimitry Andric // Disable checked locks on Darwin. Although Darwin platforms support 101349cc55cSDimitry Andric // THREADLOCAL variables they are not usable early on during process init when 102349cc55cSDimitry Andric // `__sanitizer::Mutex` is used. 103349cc55cSDimitry Andric #define SANITIZER_CHECK_DEADLOCKS \ 104*81ad6265SDimitry Andric (SANITIZER_DEBUG && !SANITIZER_GO && SANITIZER_SUPPORTS_THREADLOCAL && !SANITIZER_APPLE) 105fe6060f1SDimitry Andric 106fe6060f1SDimitry Andric #if SANITIZER_CHECK_DEADLOCKS 107fe6060f1SDimitry Andric struct MutexMeta { 108fe6060f1SDimitry Andric MutexType type; 109fe6060f1SDimitry Andric const char *name; 110fe6060f1SDimitry Andric // The table fixes what mutexes can be locked under what mutexes. 111fe6060f1SDimitry Andric // If the entry for MutexTypeFoo contains MutexTypeBar, 112fe6060f1SDimitry Andric // then Bar mutex can be locked while under Foo mutex. 113fe6060f1SDimitry Andric // Can also contain the special MutexLeaf/MutexMulti marks. 114fe6060f1SDimitry Andric MutexType can_lock[10]; 115fe6060f1SDimitry Andric }; 116fe6060f1SDimitry Andric #endif 117fe6060f1SDimitry Andric 118fe6060f1SDimitry Andric class CheckedMutex { 119fe6060f1SDimitry Andric public: CheckedMutex(MutexType type)120349cc55cSDimitry Andric explicit constexpr CheckedMutex(MutexType type) 121fe6060f1SDimitry Andric #if SANITIZER_CHECK_DEADLOCKS 122fe6060f1SDimitry Andric : type_(type) 123fe6060f1SDimitry Andric #endif 124fe6060f1SDimitry Andric { 125fe6060f1SDimitry Andric } 126fe6060f1SDimitry Andric Lock()127fe6060f1SDimitry Andric ALWAYS_INLINE void Lock() { 128fe6060f1SDimitry Andric #if SANITIZER_CHECK_DEADLOCKS 129fe6060f1SDimitry Andric LockImpl(GET_CALLER_PC()); 130fe6060f1SDimitry Andric #endif 131fe6060f1SDimitry Andric } 132fe6060f1SDimitry Andric Unlock()133fe6060f1SDimitry Andric ALWAYS_INLINE void Unlock() { 134fe6060f1SDimitry Andric #if SANITIZER_CHECK_DEADLOCKS 135fe6060f1SDimitry Andric UnlockImpl(); 136fe6060f1SDimitry Andric #endif 137fe6060f1SDimitry Andric } 138fe6060f1SDimitry Andric 139fe6060f1SDimitry Andric // Checks that the current thread does not hold any mutexes 140fe6060f1SDimitry Andric // (e.g. when returning from a runtime function to user code). CheckNoLocks()141fe6060f1SDimitry Andric static void CheckNoLocks() { 142fe6060f1SDimitry Andric #if SANITIZER_CHECK_DEADLOCKS 143fe6060f1SDimitry Andric CheckNoLocksImpl(); 144fe6060f1SDimitry Andric #endif 145fe6060f1SDimitry Andric } 146fe6060f1SDimitry Andric 147fe6060f1SDimitry Andric private: 148fe6060f1SDimitry Andric #if SANITIZER_CHECK_DEADLOCKS 149fe6060f1SDimitry Andric const MutexType type_; 150fe6060f1SDimitry Andric 151fe6060f1SDimitry Andric void LockImpl(uptr pc); 152fe6060f1SDimitry Andric void UnlockImpl(); 153fe6060f1SDimitry Andric static void CheckNoLocksImpl(); 154fe6060f1SDimitry Andric #endif 155fe6060f1SDimitry Andric }; 156fe6060f1SDimitry Andric 157fe6060f1SDimitry Andric // Reader-writer mutex. 158fe6060f1SDimitry Andric // Derive from CheckedMutex for the purposes of EBO. 159fe6060f1SDimitry Andric // We could make it a field marked with [[no_unique_address]], 160fe6060f1SDimitry Andric // but this attribute is not supported by some older compilers. 16104eeddc0SDimitry Andric class SANITIZER_MUTEX Mutex : CheckedMutex { 162fe6060f1SDimitry Andric public: 163349cc55cSDimitry Andric explicit constexpr Mutex(MutexType type = MutexUnchecked) CheckedMutex(type)164349cc55cSDimitry Andric : CheckedMutex(type) {} 165fe6060f1SDimitry Andric Lock()16604eeddc0SDimitry Andric void Lock() SANITIZER_ACQUIRE() { 167fe6060f1SDimitry Andric CheckedMutex::Lock(); 168fe6060f1SDimitry Andric u64 reset_mask = ~0ull; 169fe6060f1SDimitry Andric u64 state = atomic_load_relaxed(&state_); 170fe6060f1SDimitry Andric for (uptr spin_iters = 0;; spin_iters++) { 171fe6060f1SDimitry Andric u64 new_state; 172fe6060f1SDimitry Andric bool locked = (state & (kWriterLock | kReaderLockMask)) != 0; 173fe6060f1SDimitry Andric if (LIKELY(!locked)) { 174fe6060f1SDimitry Andric // The mutex is not read-/write-locked, try to lock. 175fe6060f1SDimitry Andric new_state = (state | kWriterLock) & reset_mask; 176fe6060f1SDimitry Andric } else if (spin_iters > kMaxSpinIters) { 177fe6060f1SDimitry Andric // We've spun enough, increment waiting writers count and block. 178fe6060f1SDimitry Andric // The counter will be decremented by whoever wakes us. 179fe6060f1SDimitry Andric new_state = (state + kWaitingWriterInc) & reset_mask; 180fe6060f1SDimitry Andric } else if ((state & kWriterSpinWait) == 0) { 181fe6060f1SDimitry Andric // Active spinning, but denote our presence so that unlocking 182fe6060f1SDimitry Andric // thread does not wake up other threads. 183fe6060f1SDimitry Andric new_state = state | kWriterSpinWait; 184fe6060f1SDimitry Andric } else { 185fe6060f1SDimitry Andric // Active spinning. 186fe6060f1SDimitry Andric state = atomic_load(&state_, memory_order_relaxed); 187fe6060f1SDimitry Andric continue; 188fe6060f1SDimitry Andric } 189fe6060f1SDimitry Andric if (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state, 190fe6060f1SDimitry Andric memory_order_acquire))) 191fe6060f1SDimitry Andric continue; 192fe6060f1SDimitry Andric if (LIKELY(!locked)) 193fe6060f1SDimitry Andric return; // We've locked the mutex. 194fe6060f1SDimitry Andric if (spin_iters > kMaxSpinIters) { 195fe6060f1SDimitry Andric // We've incremented waiting writers, so now block. 196fe6060f1SDimitry Andric writers_.Wait(); 197fe6060f1SDimitry Andric spin_iters = 0; 198fe6060f1SDimitry Andric } else { 199fe6060f1SDimitry Andric // We've set kWriterSpinWait, but we are still in active spinning. 200fe6060f1SDimitry Andric } 201fe6060f1SDimitry Andric // We either blocked and were unblocked, 202fe6060f1SDimitry Andric // or we just spun but set kWriterSpinWait. 203fe6060f1SDimitry Andric // Either way we need to reset kWriterSpinWait 204fe6060f1SDimitry Andric // next time we take the lock or block again. 205fe6060f1SDimitry Andric reset_mask = ~kWriterSpinWait; 206349cc55cSDimitry Andric state = atomic_load(&state_, memory_order_relaxed); 207349cc55cSDimitry Andric DCHECK_NE(state & kWriterSpinWait, 0); 208fe6060f1SDimitry Andric } 209fe6060f1SDimitry Andric } 210fe6060f1SDimitry Andric TryLock()211*81ad6265SDimitry Andric bool TryLock() SANITIZER_TRY_ACQUIRE(true) { 212*81ad6265SDimitry Andric u64 state = atomic_load_relaxed(&state_); 213*81ad6265SDimitry Andric for (;;) { 214*81ad6265SDimitry Andric if (UNLIKELY(state & (kWriterLock | kReaderLockMask))) 215*81ad6265SDimitry Andric return false; 216*81ad6265SDimitry Andric // The mutex is not read-/write-locked, try to lock. 217*81ad6265SDimitry Andric if (LIKELY(atomic_compare_exchange_weak( 218*81ad6265SDimitry Andric &state_, &state, state | kWriterLock, memory_order_acquire))) { 219*81ad6265SDimitry Andric CheckedMutex::Lock(); 220*81ad6265SDimitry Andric return true; 221*81ad6265SDimitry Andric } 222*81ad6265SDimitry Andric } 223*81ad6265SDimitry Andric } 224*81ad6265SDimitry Andric Unlock()22504eeddc0SDimitry Andric void Unlock() SANITIZER_RELEASE() { 226fe6060f1SDimitry Andric CheckedMutex::Unlock(); 227fe6060f1SDimitry Andric bool wake_writer; 228fe6060f1SDimitry Andric u64 wake_readers; 229fe6060f1SDimitry Andric u64 new_state; 230fe6060f1SDimitry Andric u64 state = atomic_load_relaxed(&state_); 231fe6060f1SDimitry Andric do { 232fe6060f1SDimitry Andric DCHECK_NE(state & kWriterLock, 0); 233fe6060f1SDimitry Andric DCHECK_EQ(state & kReaderLockMask, 0); 234fe6060f1SDimitry Andric new_state = state & ~kWriterLock; 235349cc55cSDimitry Andric wake_writer = (state & (kWriterSpinWait | kReaderSpinWait)) == 0 && 236349cc55cSDimitry Andric (state & kWaitingWriterMask) != 0; 237fe6060f1SDimitry Andric if (wake_writer) 238fe6060f1SDimitry Andric new_state = (new_state - kWaitingWriterInc) | kWriterSpinWait; 239fe6060f1SDimitry Andric wake_readers = 240349cc55cSDimitry Andric wake_writer || (state & kWriterSpinWait) != 0 241fe6060f1SDimitry Andric ? 0 242fe6060f1SDimitry Andric : ((state & kWaitingReaderMask) >> kWaitingReaderShift); 243fe6060f1SDimitry Andric if (wake_readers) 244349cc55cSDimitry Andric new_state = (new_state & ~kWaitingReaderMask) | kReaderSpinWait; 245fe6060f1SDimitry Andric } while (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state, 246fe6060f1SDimitry Andric memory_order_release))); 247fe6060f1SDimitry Andric if (UNLIKELY(wake_writer)) 248fe6060f1SDimitry Andric writers_.Post(); 249fe6060f1SDimitry Andric else if (UNLIKELY(wake_readers)) 250fe6060f1SDimitry Andric readers_.Post(wake_readers); 251fe6060f1SDimitry Andric } 252fe6060f1SDimitry Andric ReadLock()25304eeddc0SDimitry Andric void ReadLock() SANITIZER_ACQUIRE_SHARED() { 254fe6060f1SDimitry Andric CheckedMutex::Lock(); 255349cc55cSDimitry Andric u64 reset_mask = ~0ull; 256fe6060f1SDimitry Andric u64 state = atomic_load_relaxed(&state_); 257349cc55cSDimitry Andric for (uptr spin_iters = 0;; spin_iters++) { 258349cc55cSDimitry Andric bool locked = (state & kWriterLock) != 0; 259349cc55cSDimitry Andric u64 new_state; 260349cc55cSDimitry Andric if (LIKELY(!locked)) { 261349cc55cSDimitry Andric new_state = (state + kReaderLockInc) & reset_mask; 262349cc55cSDimitry Andric } else if (spin_iters > kMaxSpinIters) { 263349cc55cSDimitry Andric new_state = (state + kWaitingReaderInc) & reset_mask; 264349cc55cSDimitry Andric } else if ((state & kReaderSpinWait) == 0) { 265349cc55cSDimitry Andric // Active spinning, but denote our presence so that unlocking 266349cc55cSDimitry Andric // thread does not wake up other threads. 267349cc55cSDimitry Andric new_state = state | kReaderSpinWait; 268349cc55cSDimitry Andric } else { 269349cc55cSDimitry Andric // Active spinning. 270349cc55cSDimitry Andric state = atomic_load(&state_, memory_order_relaxed); 271349cc55cSDimitry Andric continue; 272349cc55cSDimitry Andric } 273349cc55cSDimitry Andric if (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state, 274349cc55cSDimitry Andric memory_order_acquire))) 275349cc55cSDimitry Andric continue; 276fe6060f1SDimitry Andric if (LIKELY(!locked)) 277349cc55cSDimitry Andric return; // We've locked the mutex. 278349cc55cSDimitry Andric if (spin_iters > kMaxSpinIters) { 279349cc55cSDimitry Andric // We've incremented waiting readers, so now block. 280fe6060f1SDimitry Andric readers_.Wait(); 281349cc55cSDimitry Andric spin_iters = 0; 282349cc55cSDimitry Andric } else { 283349cc55cSDimitry Andric // We've set kReaderSpinWait, but we are still in active spinning. 284349cc55cSDimitry Andric } 285349cc55cSDimitry Andric reset_mask = ~kReaderSpinWait; 286349cc55cSDimitry Andric state = atomic_load(&state_, memory_order_relaxed); 287349cc55cSDimitry Andric } 288fe6060f1SDimitry Andric } 289fe6060f1SDimitry Andric ReadUnlock()29004eeddc0SDimitry Andric void ReadUnlock() SANITIZER_RELEASE_SHARED() { 291fe6060f1SDimitry Andric CheckedMutex::Unlock(); 292fe6060f1SDimitry Andric bool wake; 293fe6060f1SDimitry Andric u64 new_state; 294fe6060f1SDimitry Andric u64 state = atomic_load_relaxed(&state_); 295fe6060f1SDimitry Andric do { 296fe6060f1SDimitry Andric DCHECK_NE(state & kReaderLockMask, 0); 297349cc55cSDimitry Andric DCHECK_EQ(state & kWriterLock, 0); 298fe6060f1SDimitry Andric new_state = state - kReaderLockInc; 299349cc55cSDimitry Andric wake = (new_state & 300349cc55cSDimitry Andric (kReaderLockMask | kWriterSpinWait | kReaderSpinWait)) == 0 && 301fe6060f1SDimitry Andric (new_state & kWaitingWriterMask) != 0; 302fe6060f1SDimitry Andric if (wake) 303fe6060f1SDimitry Andric new_state = (new_state - kWaitingWriterInc) | kWriterSpinWait; 304fe6060f1SDimitry Andric } while (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state, 305fe6060f1SDimitry Andric memory_order_release))); 306fe6060f1SDimitry Andric if (UNLIKELY(wake)) 307fe6060f1SDimitry Andric writers_.Post(); 308fe6060f1SDimitry Andric } 309fe6060f1SDimitry Andric 310fe6060f1SDimitry Andric // This function does not guarantee an explicit check that the calling thread 311fe6060f1SDimitry Andric // is the thread which owns the mutex. This behavior, while more strictly 312fe6060f1SDimitry Andric // correct, causes problems in cases like StopTheWorld, where a parent thread 313fe6060f1SDimitry Andric // owns the mutex but a child checks that it is locked. Rather than 314fe6060f1SDimitry Andric // maintaining complex state to work around those situations, the check only 315fe6060f1SDimitry Andric // checks that the mutex is owned. CheckWriteLocked()31604eeddc0SDimitry Andric void CheckWriteLocked() const SANITIZER_CHECK_LOCKED() { 317fe6060f1SDimitry Andric CHECK(atomic_load(&state_, memory_order_relaxed) & kWriterLock); 318fe6060f1SDimitry Andric } 319fe6060f1SDimitry Andric CheckLocked()32004eeddc0SDimitry Andric void CheckLocked() const SANITIZER_CHECK_LOCKED() { CheckWriteLocked(); } 321fe6060f1SDimitry Andric CheckReadLocked()32204eeddc0SDimitry Andric void CheckReadLocked() const SANITIZER_CHECK_LOCKED() { 323fe6060f1SDimitry Andric CHECK(atomic_load(&state_, memory_order_relaxed) & kReaderLockMask); 324fe6060f1SDimitry Andric } 325fe6060f1SDimitry Andric 326fe6060f1SDimitry Andric private: 327fe6060f1SDimitry Andric atomic_uint64_t state_ = {0}; 328fe6060f1SDimitry Andric Semaphore writers_; 329fe6060f1SDimitry Andric Semaphore readers_; 330fe6060f1SDimitry Andric 331fe6060f1SDimitry Andric // The state has 3 counters: 332fe6060f1SDimitry Andric // - number of readers holding the lock, 333fe6060f1SDimitry Andric // if non zero, the mutex is read-locked 334fe6060f1SDimitry Andric // - number of waiting readers, 335fe6060f1SDimitry Andric // if not zero, the mutex is write-locked 336fe6060f1SDimitry Andric // - number of waiting writers, 337fe6060f1SDimitry Andric // if non zero, the mutex is read- or write-locked 338fe6060f1SDimitry Andric // And 2 flags: 339fe6060f1SDimitry Andric // - writer lock 340fe6060f1SDimitry Andric // if set, the mutex is write-locked 341fe6060f1SDimitry Andric // - a writer is awake and spin-waiting 342fe6060f1SDimitry Andric // the flag is used to prevent thundering herd problem 343fe6060f1SDimitry Andric // (new writers are not woken if this flag is set) 344349cc55cSDimitry Andric // - a reader is awake and spin-waiting 345fe6060f1SDimitry Andric // 346349cc55cSDimitry Andric // Both writers and readers use active spinning before blocking. 347fe6060f1SDimitry Andric // But readers are more aggressive and always take the mutex 348fe6060f1SDimitry Andric // if there are any other readers. 349349cc55cSDimitry Andric // After wake up both writers and readers compete to lock the 350349cc55cSDimitry Andric // mutex again. This is needed to allow repeated locks even in presence 351349cc55cSDimitry Andric // of other blocked threads. 352fe6060f1SDimitry Andric static constexpr u64 kCounterWidth = 20; 353fe6060f1SDimitry Andric static constexpr u64 kReaderLockShift = 0; 354fe6060f1SDimitry Andric static constexpr u64 kReaderLockInc = 1ull << kReaderLockShift; 355fe6060f1SDimitry Andric static constexpr u64 kReaderLockMask = ((1ull << kCounterWidth) - 1) 356fe6060f1SDimitry Andric << kReaderLockShift; 357fe6060f1SDimitry Andric static constexpr u64 kWaitingReaderShift = kCounterWidth; 358fe6060f1SDimitry Andric static constexpr u64 kWaitingReaderInc = 1ull << kWaitingReaderShift; 359fe6060f1SDimitry Andric static constexpr u64 kWaitingReaderMask = ((1ull << kCounterWidth) - 1) 360fe6060f1SDimitry Andric << kWaitingReaderShift; 361fe6060f1SDimitry Andric static constexpr u64 kWaitingWriterShift = 2 * kCounterWidth; 362fe6060f1SDimitry Andric static constexpr u64 kWaitingWriterInc = 1ull << kWaitingWriterShift; 363fe6060f1SDimitry Andric static constexpr u64 kWaitingWriterMask = ((1ull << kCounterWidth) - 1) 364fe6060f1SDimitry Andric << kWaitingWriterShift; 365fe6060f1SDimitry Andric static constexpr u64 kWriterLock = 1ull << (3 * kCounterWidth); 366fe6060f1SDimitry Andric static constexpr u64 kWriterSpinWait = 1ull << (3 * kCounterWidth + 1); 367349cc55cSDimitry Andric static constexpr u64 kReaderSpinWait = 1ull << (3 * kCounterWidth + 2); 368fe6060f1SDimitry Andric 369349cc55cSDimitry Andric static constexpr uptr kMaxSpinIters = 1500; 370349cc55cSDimitry Andric 371349cc55cSDimitry Andric Mutex(LinkerInitialized) = delete; 372fe6060f1SDimitry Andric Mutex(const Mutex &) = delete; 373fe6060f1SDimitry Andric void operator=(const Mutex &) = delete; 374fe6060f1SDimitry Andric }; 375fe6060f1SDimitry Andric 376fe6060f1SDimitry Andric void FutexWait(atomic_uint32_t *p, u32 cmp); 377fe6060f1SDimitry Andric void FutexWake(atomic_uint32_t *p, u32 count); 378fe6060f1SDimitry Andric 3790b57cec5SDimitry Andric template <typename MutexType> 38004eeddc0SDimitry Andric class SANITIZER_SCOPED_LOCK GenericScopedLock { 3810b57cec5SDimitry Andric public: GenericScopedLock(MutexType * mu)38204eeddc0SDimitry Andric explicit GenericScopedLock(MutexType *mu) SANITIZER_ACQUIRE(mu) : mu_(mu) { 3830b57cec5SDimitry Andric mu_->Lock(); 3840b57cec5SDimitry Andric } 3850b57cec5SDimitry Andric SANITIZER_RELEASE()38604eeddc0SDimitry Andric ~GenericScopedLock() SANITIZER_RELEASE() { mu_->Unlock(); } 3870b57cec5SDimitry Andric 3880b57cec5SDimitry Andric private: 3890b57cec5SDimitry Andric MutexType *mu_; 3900b57cec5SDimitry Andric 391fe6060f1SDimitry Andric GenericScopedLock(const GenericScopedLock &) = delete; 392fe6060f1SDimitry Andric void operator=(const GenericScopedLock &) = delete; 3930b57cec5SDimitry Andric }; 3940b57cec5SDimitry Andric 3950b57cec5SDimitry Andric template <typename MutexType> 39604eeddc0SDimitry Andric class SANITIZER_SCOPED_LOCK GenericScopedReadLock { 3970b57cec5SDimitry Andric public: GenericScopedReadLock(MutexType * mu)39804eeddc0SDimitry Andric explicit GenericScopedReadLock(MutexType *mu) SANITIZER_ACQUIRE(mu) 39904eeddc0SDimitry Andric : mu_(mu) { 4000b57cec5SDimitry Andric mu_->ReadLock(); 4010b57cec5SDimitry Andric } 4020b57cec5SDimitry Andric SANITIZER_RELEASE()40304eeddc0SDimitry Andric ~GenericScopedReadLock() SANITIZER_RELEASE() { mu_->ReadUnlock(); } 4040b57cec5SDimitry Andric 4050b57cec5SDimitry Andric private: 4060b57cec5SDimitry Andric MutexType *mu_; 4070b57cec5SDimitry Andric 408fe6060f1SDimitry Andric GenericScopedReadLock(const GenericScopedReadLock &) = delete; 409fe6060f1SDimitry Andric void operator=(const GenericScopedReadLock &) = delete; 4100b57cec5SDimitry Andric }; 4110b57cec5SDimitry Andric 412349cc55cSDimitry Andric template <typename MutexType> 41304eeddc0SDimitry Andric class SANITIZER_SCOPED_LOCK GenericScopedRWLock { 414349cc55cSDimitry Andric public: GenericScopedRWLock(MutexType * mu,bool write)415349cc55cSDimitry Andric ALWAYS_INLINE explicit GenericScopedRWLock(MutexType *mu, bool write) 41604eeddc0SDimitry Andric SANITIZER_ACQUIRE(mu) 417349cc55cSDimitry Andric : mu_(mu), write_(write) { 418349cc55cSDimitry Andric if (write_) 419349cc55cSDimitry Andric mu_->Lock(); 420349cc55cSDimitry Andric else 421349cc55cSDimitry Andric mu_->ReadLock(); 422349cc55cSDimitry Andric } 423349cc55cSDimitry Andric ~GenericScopedRWLock()42404eeddc0SDimitry Andric ALWAYS_INLINE ~GenericScopedRWLock() SANITIZER_RELEASE() { 425349cc55cSDimitry Andric if (write_) 426349cc55cSDimitry Andric mu_->Unlock(); 427349cc55cSDimitry Andric else 428349cc55cSDimitry Andric mu_->ReadUnlock(); 429349cc55cSDimitry Andric } 430349cc55cSDimitry Andric 431349cc55cSDimitry Andric private: 432349cc55cSDimitry Andric MutexType *mu_; 433349cc55cSDimitry Andric bool write_; 434349cc55cSDimitry Andric 435349cc55cSDimitry Andric GenericScopedRWLock(const GenericScopedRWLock &) = delete; 436349cc55cSDimitry Andric void operator=(const GenericScopedRWLock &) = delete; 437349cc55cSDimitry Andric }; 438349cc55cSDimitry Andric 4390b57cec5SDimitry Andric typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock; 440fe6060f1SDimitry Andric typedef GenericScopedLock<Mutex> Lock; 441fe6060f1SDimitry Andric typedef GenericScopedReadLock<Mutex> ReadLock; 442349cc55cSDimitry Andric typedef GenericScopedRWLock<Mutex> RWLock; 4430b57cec5SDimitry Andric 4440b57cec5SDimitry Andric } // namespace __sanitizer 4450b57cec5SDimitry Andric 4460b57cec5SDimitry Andric #endif // SANITIZER_MUTEX_H 447