xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_mutex.h (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
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