xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_mutex.h (revision b3512b30dbec579da28028e29d8b33ec7242af68)
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 
20 namespace __sanitizer {
21 
22 class StaticSpinMutex {
23  public:
24   void Init() {
25     atomic_store(&state_, 0, memory_order_relaxed);
26   }
27 
28   void Lock() {
29     if (TryLock())
30       return;
31     LockSlow();
32   }
33 
34   bool TryLock() {
35     return atomic_exchange(&state_, 1, memory_order_acquire) == 0;
36   }
37 
38   void Unlock() {
39     atomic_store(&state_, 0, memory_order_release);
40   }
41 
42   void CheckLocked() {
43     CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1);
44   }
45 
46  private:
47   atomic_uint8_t state_;
48 
49   void NOINLINE LockSlow() {
50     for (int i = 0;; i++) {
51       if (i < 10)
52         proc_yield(10);
53       else
54         internal_sched_yield();
55       if (atomic_load(&state_, memory_order_relaxed) == 0
56           && atomic_exchange(&state_, 1, memory_order_acquire) == 0)
57         return;
58     }
59   }
60 };
61 
62 class SpinMutex : public StaticSpinMutex {
63  public:
64   SpinMutex() {
65     Init();
66   }
67 
68  private:
69   SpinMutex(const SpinMutex&);
70   void operator=(const SpinMutex&);
71 };
72 
73 class BlockingMutex {
74  public:
75   explicit constexpr BlockingMutex(LinkerInitialized)
76       : opaque_storage_ {0, }, owner_ {0} {}
77   BlockingMutex();
78   void Lock();
79   void Unlock();
80 
81   // This function does not guarantee an explicit check that the calling thread
82   // is the thread which owns the mutex. This behavior, while more strictly
83   // correct, causes problems in cases like StopTheWorld, where a parent thread
84   // owns the mutex but a child checks that it is locked. Rather than
85   // maintaining complex state to work around those situations, the check only
86   // checks that the mutex is owned, and assumes callers to be generally
87   // well-behaved.
88   void CheckLocked();
89 
90  private:
91   // Solaris mutex_t has a member that requires 64-bit alignment.
92   ALIGNED(8) uptr opaque_storage_[10];
93   uptr owner_;  // for debugging
94 };
95 
96 // Reader-writer spin mutex.
97 class RWMutex {
98  public:
99   RWMutex() {
100     atomic_store(&state_, kUnlocked, memory_order_relaxed);
101   }
102 
103   ~RWMutex() {
104     CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);
105   }
106 
107   void Lock() {
108     u32 cmp = kUnlocked;
109     if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
110                                        memory_order_acquire))
111       return;
112     LockSlow();
113   }
114 
115   void Unlock() {
116     u32 prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
117     DCHECK_NE(prev & kWriteLock, 0);
118     (void)prev;
119   }
120 
121   void ReadLock() {
122     u32 prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
123     if ((prev & kWriteLock) == 0)
124       return;
125     ReadLockSlow();
126   }
127 
128   void ReadUnlock() {
129     u32 prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);
130     DCHECK_EQ(prev & kWriteLock, 0);
131     DCHECK_GT(prev & ~kWriteLock, 0);
132     (void)prev;
133   }
134 
135   void CheckLocked() {
136     CHECK_NE(atomic_load(&state_, memory_order_relaxed), kUnlocked);
137   }
138 
139  private:
140   atomic_uint32_t state_;
141 
142   enum {
143     kUnlocked = 0,
144     kWriteLock = 1,
145     kReadLock = 2
146   };
147 
148   void NOINLINE LockSlow() {
149     for (int i = 0;; i++) {
150       if (i < 10)
151         proc_yield(10);
152       else
153         internal_sched_yield();
154       u32 cmp = atomic_load(&state_, memory_order_relaxed);
155       if (cmp == kUnlocked &&
156           atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,
157                                        memory_order_acquire))
158           return;
159     }
160   }
161 
162   void NOINLINE ReadLockSlow() {
163     for (int i = 0;; i++) {
164       if (i < 10)
165         proc_yield(10);
166       else
167         internal_sched_yield();
168       u32 prev = atomic_load(&state_, memory_order_acquire);
169       if ((prev & kWriteLock) == 0)
170         return;
171     }
172   }
173 
174   RWMutex(const RWMutex&);
175   void operator = (const RWMutex&);
176 };
177 
178 template<typename MutexType>
179 class GenericScopedLock {
180  public:
181   explicit GenericScopedLock(MutexType *mu)
182       : mu_(mu) {
183     mu_->Lock();
184   }
185 
186   ~GenericScopedLock() {
187     mu_->Unlock();
188   }
189 
190  private:
191   MutexType *mu_;
192 
193   GenericScopedLock(const GenericScopedLock&);
194   void operator=(const GenericScopedLock&);
195 };
196 
197 template<typename MutexType>
198 class GenericScopedReadLock {
199  public:
200   explicit GenericScopedReadLock(MutexType *mu)
201       : mu_(mu) {
202     mu_->ReadLock();
203   }
204 
205   ~GenericScopedReadLock() {
206     mu_->ReadUnlock();
207   }
208 
209  private:
210   MutexType *mu_;
211 
212   GenericScopedReadLock(const GenericScopedReadLock&);
213   void operator=(const GenericScopedReadLock&);
214 };
215 
216 typedef GenericScopedLock<StaticSpinMutex> SpinMutexLock;
217 typedef GenericScopedLock<BlockingMutex> BlockingMutexLock;
218 typedef GenericScopedLock<RWMutex> RWMutexLock;
219 typedef GenericScopedReadLock<RWMutex> RWMutexReadLock;
220 
221 }  // namespace __sanitizer
222 
223 #endif  // SANITIZER_MUTEX_H
224