[clang] Handle __declspec() attributes in using
[llvm-project.git] / compiler-rt / lib / sanitizer_common / sanitizer_mutex.h
blobb1a58e421d81a5e1da1baf5a9960c6265d14182a
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.
11 //===----------------------------------------------------------------------===//
13 #ifndef SANITIZER_MUTEX_H
14 #define SANITIZER_MUTEX_H
16 #include "sanitizer_atomic.h"
17 #include "sanitizer_internal_defs.h"
18 #include "sanitizer_libc.h"
19 #include "sanitizer_thread_safety.h"
21 namespace __sanitizer {
23 class SANITIZER_MUTEX StaticSpinMutex {
24 public:
25 void Init() {
26 atomic_store(&state_, 0, memory_order_relaxed);
29 void Lock() SANITIZER_ACQUIRE() {
30 if (LIKELY(TryLock()))
31 return;
32 LockSlow();
35 bool TryLock() SANITIZER_TRY_ACQUIRE(true) {
36 return atomic_exchange(&state_, 1, memory_order_acquire) == 0;
39 void Unlock() SANITIZER_RELEASE() {
40 atomic_store(&state_, 0, memory_order_release);
43 void CheckLocked() const SANITIZER_CHECK_LOCKED() {
44 CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1);
47 private:
48 atomic_uint8_t state_;
50 void LockSlow();
53 class SANITIZER_MUTEX SpinMutex : public StaticSpinMutex {
54 public:
55 SpinMutex() {
56 Init();
59 SpinMutex(const SpinMutex &) = delete;
60 void operator=(const SpinMutex &) = delete;
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;
72 void Wait();
73 void Post(u32 count = 1);
75 private:
76 atomic_uint32_t state_ = {0};
79 typedef int MutexType;
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,
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)
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];
116 #endif
118 class CheckedMutex {
119 public:
120 explicit constexpr CheckedMutex(MutexType type)
121 #if SANITIZER_CHECK_DEADLOCKS
122 : type_(type)
123 #endif
127 ALWAYS_INLINE void Lock() {
128 #if SANITIZER_CHECK_DEADLOCKS
129 LockImpl(GET_CALLER_PC());
130 #endif
133 ALWAYS_INLINE void Unlock() {
134 #if SANITIZER_CHECK_DEADLOCKS
135 UnlockImpl();
136 #endif
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
147 private:
148 #if SANITIZER_CHECK_DEADLOCKS
149 const MutexType type_;
151 void LockImpl(uptr pc);
152 void UnlockImpl();
153 static void CheckNoLocksImpl();
154 #endif
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) {}
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;
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.
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);
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;
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
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);
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;
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.
285 reset_mask = ~kReaderSpinWait;
286 state = atomic_load(&state_, memory_order_relaxed);
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();
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);
320 void CheckLocked() const SANITIZER_CHECK_LOCKED() { CheckWriteLocked(); }
322 void CheckReadLocked() const SANITIZER_CHECK_LOCKED() {
323 CHECK(atomic_load(&state_, memory_order_relaxed) & kReaderLockMask);
326 private:
327 atomic_uint64_t state_ = {0};
328 Semaphore writers_;
329 Semaphore readers_;
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
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);
369 static constexpr uptr kMaxSpinIters = 1500;
371 Mutex(LinkerInitialized) = delete;
372 Mutex(const Mutex &) = delete;
373 void operator=(const Mutex &) = delete;
376 void FutexWait(atomic_uint32_t *p, u32 cmp);
377 void FutexWake(atomic_uint32_t *p, u32 count);
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();
386 ~GenericScopedLock() SANITIZER_RELEASE() { mu_->Unlock(); }
388 private:
389 MutexType *mu_;
391 GenericScopedLock(const GenericScopedLock &) = delete;
392 void operator=(const GenericScopedLock &) = delete;
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();
403 ~GenericScopedReadLock() SANITIZER_RELEASE() { mu_->ReadUnlock(); }
405 private:
406 MutexType *mu_;
408 GenericScopedReadLock(const GenericScopedReadLock &) = delete;
409 void operator=(const GenericScopedReadLock &) = delete;
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();
424 ALWAYS_INLINE ~GenericScopedRWLock() SANITIZER_RELEASE() {
425 if (write_)
426 mu_->Unlock();
427 else
428 mu_->ReadUnlock();
431 private:
432 MutexType *mu_;
433 bool write_;
435 GenericScopedRWLock(const GenericScopedRWLock &) = delete;
436 void operator=(const GenericScopedRWLock &) = delete;
439 typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
440 typedef GenericScopedLock<Mutex> Lock;
441 typedef GenericScopedReadLock<Mutex> ReadLock;
442 typedef GenericScopedRWLock<Mutex> RWLock;
444 } // namespace __sanitizer
446 #endif // SANITIZER_MUTEX_H