xref: /netbsd-src/external/gpl3/gcc/dist/libsanitizer/sanitizer_common/sanitizer_mutex.h (revision e9e6e0f6fbc36b8de7586170291cf5fc97cab8b6)
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 MUTEX StaticSpinMutex {
24  public:
Init()25   void Init() {
26     atomic_store(&state_, 0, memory_order_relaxed);
27   }
28 
Lock()29   void Lock() ACQUIRE() {
30     if (LIKELY(TryLock()))
31       return;
32     LockSlow();
33   }
34 
TryLock()35   bool TryLock() TRY_ACQUIRE(true) {
36     return atomic_exchange(&state_, 1, memory_order_acquire) == 0;
37   }
38 
Unlock()39   void Unlock() RELEASE() { atomic_store(&state_, 0, memory_order_release); }
40 
CheckLocked()41   void CheckLocked() const CHECK_LOCKED() {
42     CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1);
43   }
44 
45  private:
46   atomic_uint8_t state_;
47 
48   void LockSlow();
49 };
50 
51 class MUTEX SpinMutex : public StaticSpinMutex {
52  public:
SpinMutex()53   SpinMutex() {
54     Init();
55   }
56 
57   SpinMutex(const SpinMutex &) = delete;
58   void operator=(const SpinMutex &) = delete;
59 };
60 
61 // Semaphore provides an OS-dependent way to park/unpark threads.
62 // The last thread returned from Wait can destroy the object
63 // (destruction-safety).
64 class Semaphore {
65  public:
Semaphore()66   constexpr Semaphore() {}
67   Semaphore(const Semaphore &) = delete;
68   void operator=(const Semaphore &) = delete;
69 
70   void Wait();
71   void Post(u32 count = 1);
72 
73  private:
74   atomic_uint32_t state_ = {0};
75 };
76 
77 typedef int MutexType;
78 
79 enum {
80   // Used as sentinel and to catch unassigned types
81   // (should not be used as real Mutex type).
82   MutexInvalid = 0,
83   MutexThreadRegistry,
84   // Each tool own mutexes must start at this number.
85   MutexLastCommon,
86   // Type for legacy mutexes that are not checked for deadlocks.
87   MutexUnchecked = -1,
88   // Special marks that can be used in MutexMeta::can_lock table.
89   // The leaf mutexes can be locked under any other non-leaf mutex,
90   // but no other mutex can be locked while under a leaf mutex.
91   MutexLeaf = -1,
92   // Multiple mutexes of this type can be locked at the same time.
93   MutexMulti = -3,
94 };
95 
96 // Go linker does not support THREADLOCAL variables,
97 // so we can't use per-thread state.
98 // Disable checked locks on Darwin. Although Darwin platforms support
99 // THREADLOCAL variables they are not usable early on during process init when
100 // `__sanitizer::Mutex` is used.
101 #define SANITIZER_CHECK_DEADLOCKS \
102   (SANITIZER_DEBUG && !SANITIZER_GO && SANITIZER_SUPPORTS_THREADLOCAL && !SANITIZER_MAC)
103 
104 #if SANITIZER_CHECK_DEADLOCKS
105 struct MutexMeta {
106   MutexType type;
107   const char *name;
108   // The table fixes what mutexes can be locked under what mutexes.
109   // If the entry for MutexTypeFoo contains MutexTypeBar,
110   // then Bar mutex can be locked while under Foo mutex.
111   // Can also contain the special MutexLeaf/MutexMulti marks.
112   MutexType can_lock[10];
113 };
114 #endif
115 
116 class CheckedMutex {
117  public:
CheckedMutex(MutexType type)118   explicit constexpr CheckedMutex(MutexType type)
119 #if SANITIZER_CHECK_DEADLOCKS
120       : type_(type)
121 #endif
122   {
123   }
124 
Lock()125   ALWAYS_INLINE void Lock() {
126 #if SANITIZER_CHECK_DEADLOCKS
127     LockImpl(GET_CALLER_PC());
128 #endif
129   }
130 
Unlock()131   ALWAYS_INLINE void Unlock() {
132 #if SANITIZER_CHECK_DEADLOCKS
133     UnlockImpl();
134 #endif
135   }
136 
137   // Checks that the current thread does not hold any mutexes
138   // (e.g. when returning from a runtime function to user code).
CheckNoLocks()139   static void CheckNoLocks() {
140 #if SANITIZER_CHECK_DEADLOCKS
141     CheckNoLocksImpl();
142 #endif
143   }
144 
145  private:
146 #if SANITIZER_CHECK_DEADLOCKS
147   const MutexType type_;
148 
149   void LockImpl(uptr pc);
150   void UnlockImpl();
151   static void CheckNoLocksImpl();
152 #endif
153 };
154 
155 // Reader-writer mutex.
156 // Derive from CheckedMutex for the purposes of EBO.
157 // We could make it a field marked with [[no_unique_address]],
158 // but this attribute is not supported by some older compilers.
159 class MUTEX Mutex : CheckedMutex {
160  public:
161   explicit constexpr Mutex(MutexType type = MutexUnchecked)
CheckedMutex(type)162       : CheckedMutex(type) {}
163 
Lock()164   void Lock() ACQUIRE() {
165     CheckedMutex::Lock();
166     u64 reset_mask = ~0ull;
167     u64 state = atomic_load_relaxed(&state_);
168     for (uptr spin_iters = 0;; spin_iters++) {
169       u64 new_state;
170       bool locked = (state & (kWriterLock | kReaderLockMask)) != 0;
171       if (LIKELY(!locked)) {
172         // The mutex is not read-/write-locked, try to lock.
173         new_state = (state | kWriterLock) & reset_mask;
174       } else if (spin_iters > kMaxSpinIters) {
175         // We've spun enough, increment waiting writers count and block.
176         // The counter will be decremented by whoever wakes us.
177         new_state = (state + kWaitingWriterInc) & reset_mask;
178       } else if ((state & kWriterSpinWait) == 0) {
179         // Active spinning, but denote our presence so that unlocking
180         // thread does not wake up other threads.
181         new_state = state | kWriterSpinWait;
182       } else {
183         // Active spinning.
184         state = atomic_load(&state_, memory_order_relaxed);
185         continue;
186       }
187       if (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state,
188                                                  memory_order_acquire)))
189         continue;
190       if (LIKELY(!locked))
191         return;  // We've locked the mutex.
192       if (spin_iters > kMaxSpinIters) {
193         // We've incremented waiting writers, so now block.
194         writers_.Wait();
195         spin_iters = 0;
196       } else {
197         // We've set kWriterSpinWait, but we are still in active spinning.
198       }
199       // We either blocked and were unblocked,
200       // or we just spun but set kWriterSpinWait.
201       // Either way we need to reset kWriterSpinWait
202       // next time we take the lock or block again.
203       reset_mask = ~kWriterSpinWait;
204       state = atomic_load(&state_, memory_order_relaxed);
205       DCHECK_NE(state & kWriterSpinWait, 0);
206     }
207   }
208 
Unlock()209   void Unlock() RELEASE() {
210     CheckedMutex::Unlock();
211     bool wake_writer;
212     u64 wake_readers;
213     u64 new_state;
214     u64 state = atomic_load_relaxed(&state_);
215     do {
216       DCHECK_NE(state & kWriterLock, 0);
217       DCHECK_EQ(state & kReaderLockMask, 0);
218       new_state = state & ~kWriterLock;
219       wake_writer = (state & (kWriterSpinWait | kReaderSpinWait)) == 0 &&
220                     (state & kWaitingWriterMask) != 0;
221       if (wake_writer)
222         new_state = (new_state - kWaitingWriterInc) | kWriterSpinWait;
223       wake_readers =
224           wake_writer || (state & kWriterSpinWait) != 0
225               ? 0
226               : ((state & kWaitingReaderMask) >> kWaitingReaderShift);
227       if (wake_readers)
228         new_state = (new_state & ~kWaitingReaderMask) | kReaderSpinWait;
229     } while (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state,
230                                                     memory_order_release)));
231     if (UNLIKELY(wake_writer))
232       writers_.Post();
233     else if (UNLIKELY(wake_readers))
234       readers_.Post(wake_readers);
235   }
236 
ReadLock()237   void ReadLock() ACQUIRE_SHARED() {
238     CheckedMutex::Lock();
239     u64 reset_mask = ~0ull;
240     u64 state = atomic_load_relaxed(&state_);
241     for (uptr spin_iters = 0;; spin_iters++) {
242       bool locked = (state & kWriterLock) != 0;
243       u64 new_state;
244       if (LIKELY(!locked)) {
245         new_state = (state + kReaderLockInc) & reset_mask;
246       } else if (spin_iters > kMaxSpinIters) {
247         new_state = (state + kWaitingReaderInc) & reset_mask;
248       } else if ((state & kReaderSpinWait) == 0) {
249         // Active spinning, but denote our presence so that unlocking
250         // thread does not wake up other threads.
251         new_state = state | kReaderSpinWait;
252       } else {
253         // Active spinning.
254         state = atomic_load(&state_, memory_order_relaxed);
255         continue;
256       }
257       if (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state,
258                                                  memory_order_acquire)))
259         continue;
260       if (LIKELY(!locked))
261         return;  // We've locked the mutex.
262       if (spin_iters > kMaxSpinIters) {
263         // We've incremented waiting readers, so now block.
264         readers_.Wait();
265         spin_iters = 0;
266       } else {
267         // We've set kReaderSpinWait, but we are still in active spinning.
268       }
269       reset_mask = ~kReaderSpinWait;
270       state = atomic_load(&state_, memory_order_relaxed);
271     }
272   }
273 
ReadUnlock()274   void ReadUnlock() RELEASE_SHARED() {
275     CheckedMutex::Unlock();
276     bool wake;
277     u64 new_state;
278     u64 state = atomic_load_relaxed(&state_);
279     do {
280       DCHECK_NE(state & kReaderLockMask, 0);
281       DCHECK_EQ(state & kWriterLock, 0);
282       new_state = state - kReaderLockInc;
283       wake = (new_state &
284               (kReaderLockMask | kWriterSpinWait | kReaderSpinWait)) == 0 &&
285              (new_state & kWaitingWriterMask) != 0;
286       if (wake)
287         new_state = (new_state - kWaitingWriterInc) | kWriterSpinWait;
288     } while (UNLIKELY(!atomic_compare_exchange_weak(&state_, &state, new_state,
289                                                     memory_order_release)));
290     if (UNLIKELY(wake))
291       writers_.Post();
292   }
293 
294   // This function does not guarantee an explicit check that the calling thread
295   // is the thread which owns the mutex. This behavior, while more strictly
296   // correct, causes problems in cases like StopTheWorld, where a parent thread
297   // owns the mutex but a child checks that it is locked. Rather than
298   // maintaining complex state to work around those situations, the check only
299   // checks that the mutex is owned.
CheckWriteLocked()300   void CheckWriteLocked() const CHECK_LOCKED() {
301     CHECK(atomic_load(&state_, memory_order_relaxed) & kWriterLock);
302   }
303 
CheckLocked()304   void CheckLocked() const CHECK_LOCKED() { CheckWriteLocked(); }
305 
CheckReadLocked()306   void CheckReadLocked() const CHECK_LOCKED() {
307     CHECK(atomic_load(&state_, memory_order_relaxed) & kReaderLockMask);
308   }
309 
310  private:
311   atomic_uint64_t state_ = {0};
312   Semaphore writers_;
313   Semaphore readers_;
314 
315   // The state has 3 counters:
316   //  - number of readers holding the lock,
317   //    if non zero, the mutex is read-locked
318   //  - number of waiting readers,
319   //    if not zero, the mutex is write-locked
320   //  - number of waiting writers,
321   //    if non zero, the mutex is read- or write-locked
322   // And 2 flags:
323   //  - writer lock
324   //    if set, the mutex is write-locked
325   //  - a writer is awake and spin-waiting
326   //    the flag is used to prevent thundering herd problem
327   //    (new writers are not woken if this flag is set)
328   //  - a reader is awake and spin-waiting
329   //
330   // Both writers and readers use active spinning before blocking.
331   // But readers are more aggressive and always take the mutex
332   // if there are any other readers.
333   // After wake up both writers and readers compete to lock the
334   // mutex again. This is needed to allow repeated locks even in presence
335   // of other blocked threads.
336   static constexpr u64 kCounterWidth = 20;
337   static constexpr u64 kReaderLockShift = 0;
338   static constexpr u64 kReaderLockInc = 1ull << kReaderLockShift;
339   static constexpr u64 kReaderLockMask = ((1ull << kCounterWidth) - 1)
340                                          << kReaderLockShift;
341   static constexpr u64 kWaitingReaderShift = kCounterWidth;
342   static constexpr u64 kWaitingReaderInc = 1ull << kWaitingReaderShift;
343   static constexpr u64 kWaitingReaderMask = ((1ull << kCounterWidth) - 1)
344                                             << kWaitingReaderShift;
345   static constexpr u64 kWaitingWriterShift = 2 * kCounterWidth;
346   static constexpr u64 kWaitingWriterInc = 1ull << kWaitingWriterShift;
347   static constexpr u64 kWaitingWriterMask = ((1ull << kCounterWidth) - 1)
348                                             << kWaitingWriterShift;
349   static constexpr u64 kWriterLock = 1ull << (3 * kCounterWidth);
350   static constexpr u64 kWriterSpinWait = 1ull << (3 * kCounterWidth + 1);
351   static constexpr u64 kReaderSpinWait = 1ull << (3 * kCounterWidth + 2);
352 
353   static constexpr uptr kMaxSpinIters = 1500;
354 
355   Mutex(LinkerInitialized) = delete;
356   Mutex(const Mutex &) = delete;
357   void operator=(const Mutex &) = delete;
358 };
359 
360 void FutexWait(atomic_uint32_t *p, u32 cmp);
361 void FutexWake(atomic_uint32_t *p, u32 count);
362 
363 template <typename MutexType>
364 class SCOPED_LOCK GenericScopedLock {
365  public:
GenericScopedLock(MutexType * mu)366   explicit GenericScopedLock(MutexType *mu) ACQUIRE(mu) : mu_(mu) {
367     mu_->Lock();
368   }
369 
RELEASE()370   ~GenericScopedLock() RELEASE() { mu_->Unlock(); }
371 
372  private:
373   MutexType *mu_;
374 
375   GenericScopedLock(const GenericScopedLock &) = delete;
376   void operator=(const GenericScopedLock &) = delete;
377 };
378 
379 template <typename MutexType>
380 class SCOPED_LOCK GenericScopedReadLock {
381  public:
GenericScopedReadLock(MutexType * mu)382   explicit GenericScopedReadLock(MutexType *mu) ACQUIRE(mu) : mu_(mu) {
383     mu_->ReadLock();
384   }
385 
RELEASE()386   ~GenericScopedReadLock() RELEASE() { mu_->ReadUnlock(); }
387 
388  private:
389   MutexType *mu_;
390 
391   GenericScopedReadLock(const GenericScopedReadLock &) = delete;
392   void operator=(const GenericScopedReadLock &) = delete;
393 };
394 
395 template <typename MutexType>
396 class SCOPED_LOCK GenericScopedRWLock {
397  public:
GenericScopedRWLock(MutexType * mu,bool write)398   ALWAYS_INLINE explicit GenericScopedRWLock(MutexType *mu, bool write)
399       ACQUIRE(mu)
400       : mu_(mu), write_(write) {
401     if (write_)
402       mu_->Lock();
403     else
404       mu_->ReadLock();
405   }
406 
~GenericScopedRWLock()407   ALWAYS_INLINE ~GenericScopedRWLock() RELEASE() {
408     if (write_)
409       mu_->Unlock();
410     else
411       mu_->ReadUnlock();
412   }
413 
414  private:
415   MutexType *mu_;
416   bool write_;
417 
418   GenericScopedRWLock(const GenericScopedRWLock &) = delete;
419   void operator=(const GenericScopedRWLock &) = delete;
420 };
421 
422 typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
423 typedef GenericScopedLock<Mutex> Lock;
424 typedef GenericScopedReadLock<Mutex> ReadLock;
425 typedef GenericScopedRWLock<Mutex> RWLock;
426 
427 }  // namespace __sanitizer
428 
429 #endif  // SANITIZER_MUTEX_H
430