1 // Copyright (C) 2014 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #pragma once
16
17 #include "aemu/base/Compiler.h"
18
19 #include "aemu/base/ThreadAnnotations.h"
20
21 #include <atomic>
22
23 #ifdef _WIN32
24 #define WIN32_LEAN_AND_MEAN 1
25 #include <windows.h>
26 #else
27 #include <pthread.h>
28 #endif
29
30 #include <assert.h>
31
32 namespace android {
33 namespace base {
34
35 class AutoLock;
36 class AutoWriteLock;
37 class AutoReadLock;
38
39 // A wrapper class for mutexes only suitable for using in static context,
40 // where it's OK to leak the underlying system object. Use Lock for scoped or
41 // member locks.
42 class CAPABILITY("mutex") StaticLock {
43 public:
44 using AutoLock = android::base::AutoLock;
45
46 constexpr StaticLock() = default;
47
48 // Acquire the lock.
lock()49 void lock() ACQUIRE() {
50 #ifdef _WIN32
51 ::AcquireSRWLockExclusive(&mLock);
52 #else
53 ::pthread_mutex_lock(&mLock);
54 #endif
55 }
56
tryLock()57 bool tryLock() TRY_ACQUIRE(true) {
58 bool ret = false;
59 #ifdef _WIN32
60 ret = ::TryAcquireSRWLockExclusive(&mLock);
61 #else
62 ret = ::pthread_mutex_trylock(&mLock) == 0;
63 #endif
64 return ret;
65 }
66
67 // Release the lock.
unlock()68 void unlock() RELEASE() {
69 #ifdef _WIN32
70 ::ReleaseSRWLockExclusive(&mLock);
71 #else
72 ::pthread_mutex_unlock(&mLock);
73 #endif
74 }
75
76 protected:
77 friend class ConditionVariable;
78
79 #ifdef _WIN32
80 // Benchmarks show that on Windows SRWLOCK performs a little bit better than
81 // CRITICAL_SECTION for uncontended mode and much better in case of
82 // contention.
83 SRWLOCK mLock = SRWLOCK_INIT;
84 #else
85 pthread_mutex_t mLock = PTHREAD_MUTEX_INITIALIZER;
86 #endif
87 // Both POSIX threads and WinAPI don't allow move (undefined behavior).
88 DISALLOW_COPY_ASSIGN_AND_MOVE(StaticLock);
89 };
90
91 // Simple wrapper class for mutexes used in non-static context.
92 class Lock : public StaticLock {
93 public:
94 using StaticLock::AutoLock;
95
96 constexpr Lock() = default;
97 #ifndef _WIN32
98 // The only difference is that POSIX requires a deallocation function call
99 // for its mutexes.
~Lock()100 ~Lock() { ::pthread_mutex_destroy(&mLock); }
101 #endif
102 };
103
104 class ReadWriteLock {
105 public:
106 using AutoWriteLock = android::base::AutoWriteLock;
107 using AutoReadLock = android::base::AutoReadLock;
108
109 #ifdef _WIN32
110 constexpr ReadWriteLock() = default;
111 ~ReadWriteLock() = default;
lockRead()112 void lockRead() { ::AcquireSRWLockShared(&mLock); }
unlockRead()113 void unlockRead() { ::ReleaseSRWLockShared(&mLock); }
lockWrite()114 void lockWrite() { ::AcquireSRWLockExclusive(&mLock); }
unlockWrite()115 void unlockWrite() { ::ReleaseSRWLockExclusive(&mLock); }
116
117 private:
118 SRWLOCK mLock = SRWLOCK_INIT;
119 #else // !_WIN32
ReadWriteLock()120 ReadWriteLock() { ::pthread_rwlock_init(&mLock, NULL); }
~ReadWriteLock()121 ~ReadWriteLock() { ::pthread_rwlock_destroy(&mLock); }
lockRead()122 void lockRead() { ::pthread_rwlock_rdlock(&mLock); }
unlockRead()123 void unlockRead() { ::pthread_rwlock_unlock(&mLock); }
lockWrite()124 void lockWrite() { ::pthread_rwlock_wrlock(&mLock); }
unlockWrite()125 void unlockWrite() { ::pthread_rwlock_unlock(&mLock); }
126
127 private:
128 pthread_rwlock_t mLock;
129 #endif // !_WIN32
130
131 friend class ConditionVariable;
132 DISALLOW_COPY_ASSIGN_AND_MOVE(ReadWriteLock);
133 };
134
135 // Helper class to lock / unlock a mutex automatically on scope
136 // entry and exit.
137 // NB: not thread-safe (as opposed to the Lock class)
138 class SCOPED_CAPABILITY AutoLock {
139 public:
AutoLock(StaticLock & lock)140 AutoLock(StaticLock& lock) ACQUIRE(mLock) : mLock(lock) { mLock.lock(); }
141
AutoLock(AutoLock && other)142 AutoLock(AutoLock&& other) : mLock(other.mLock), mLocked(other.mLocked) {
143 other.mLocked = false;
144 }
145
lock()146 void lock() ACQUIRE(mLock) {
147 assert(!mLocked);
148 mLock.lock();
149 mLocked = true;
150 }
151
unlock()152 void unlock() RELEASE(mLock) {
153 assert(mLocked);
154 mLock.unlock();
155 mLocked = false;
156 }
157
isLocked()158 bool isLocked() const { return mLocked; }
159
RELEASE()160 ~AutoLock() RELEASE() {
161 if (mLocked) {
162 mLock.unlock();
163 }
164 }
165
166 private:
167 StaticLock& mLock;
168 bool mLocked = true;
169
170 friend class ConditionVariable;
171 // Don't allow move because this class has a non-movable object.
172 DISALLOW_COPY_AND_ASSIGN(AutoLock);
173 };
174
175 class AutoWriteLock {
176 public:
AutoWriteLock(ReadWriteLock & lock)177 AutoWriteLock(ReadWriteLock& lock) : mLock(lock) { mLock.lockWrite(); }
178
lockWrite()179 void lockWrite() {
180 assert(!mWriteLocked);
181 mLock.lockWrite();
182 mWriteLocked = true;
183 }
184
unlockWrite()185 void unlockWrite() {
186 assert(mWriteLocked);
187 mLock.unlockWrite();
188 mWriteLocked = false;
189 }
190
~AutoWriteLock()191 ~AutoWriteLock() {
192 if (mWriteLocked) {
193 mLock.unlockWrite();
194 }
195 }
196
197 private:
198 ReadWriteLock& mLock;
199 bool mWriteLocked = true;
200 // This class has a non-movable object.
201 DISALLOW_COPY_ASSIGN_AND_MOVE(AutoWriteLock);
202 };
203
204 class AutoReadLock {
205 public:
AutoReadLock(ReadWriteLock & lock)206 AutoReadLock(ReadWriteLock& lock) : mLock(lock) { mLock.lockRead(); }
207
lockRead()208 void lockRead() {
209 assert(!mReadLocked);
210 mLock.lockRead();
211 mReadLocked = true;
212 }
213
unlockRead()214 void unlockRead() {
215 assert(mReadLocked);
216 mLock.unlockRead();
217 mReadLocked = false;
218 }
219
~AutoReadLock()220 ~AutoReadLock() {
221 if (mReadLocked) {
222 mLock.unlockRead();
223 }
224 }
225
226 private:
227 ReadWriteLock& mLock;
228 bool mReadLocked = true;
229 // This class has a non-movable object.
230 DISALLOW_COPY_ASSIGN_AND_MOVE(AutoReadLock);
231 };
232
233 // Sequence lock implementation
234 // See https://en.wikipedia.org/wiki/Seqlock for more info
SmpWmb()235 static inline __attribute__((always_inline)) void SmpWmb() {
236 #if defined(__aarch64__)
237 asm volatile("dmb ishst" ::: "memory");
238 #elif defined(__x86_64__)
239 std::atomic_thread_fence(std::memory_order_release);
240 #elif defined(__riscv) && (__riscv_xlen == 64)
241 std::atomic_thread_fence(std::memory_order_release);
242 #else
243 #error "Unimplemented SmpWmb for current CPU architecture"
244 #endif
245 }
246
SmpRmb()247 static inline __attribute__((always_inline)) void SmpRmb() {
248 #if defined(__aarch64__)
249 asm volatile("dmb ishld" ::: "memory");
250 #elif defined(__x86_64__)
251 std::atomic_thread_fence(std::memory_order_acquire);
252 #elif defined(__riscv) && (__riscv_xlen == 64)
253 std::atomic_thread_fence(std::memory_order_acquire);
254 #else
255 #error "Unimplemented SmpRmb for current CPU architecture"
256 #endif
257 }
258
259 class SeqLock {
260 public:
beginWrite()261 void beginWrite() ACQUIRE(mWriteLock) {
262 mWriteLock.lock();
263 mSeq.fetch_add(1, std::memory_order_release);
264 SmpWmb();
265 }
266
endWrite()267 void endWrite() RELEASE(mWriteLock) {
268 SmpWmb();
269 mSeq.fetch_add(1, std::memory_order_release);
270 mWriteLock.unlock();
271 }
272
273 #ifdef __cplusplus
274 # define SEQLOCK_LIKELY( exp ) (__builtin_expect( !!(exp), true ))
275 # define SEQLOCK_UNLIKELY( exp ) (__builtin_expect( !!(exp), false ))
276 #else
277 # define SEQLOCK_LIKELY( exp ) (__builtin_expect( !!(exp), 1 ))
278 # define SEQLOCK_UNLIKELY( exp ) (__builtin_expect( !!(exp), 0 ))
279 #endif
280
beginRead()281 uint32_t beginRead() {
282 uint32_t res;
283
284 repeat:
285 res = mSeq.load(std::memory_order_acquire);
286 if (SEQLOCK_UNLIKELY(res & 1)) {
287 goto repeat;
288 }
289
290 SmpRmb();
291 return res;
292 }
293
shouldRetryRead(uint32_t prevSeq)294 bool shouldRetryRead(uint32_t prevSeq) {
295 SmpRmb();
296 uint32_t res = mSeq.load(std::memory_order_acquire);
297 return (res != prevSeq);
298 }
299
300 // Convenience class for write
301 class ScopedWrite {
302 public:
ScopedWrite(SeqLock * lock)303 ScopedWrite(SeqLock* lock) : mLock(lock) {
304 mLock->beginWrite();
305 }
~ScopedWrite()306 ~ScopedWrite() {
307 mLock->endWrite();
308 }
309 private:
310 SeqLock* mLock;
311 };
312
313 // Convenience macro for read (no std::function due to its considerable overhead)
314 #define AEMU_SEQLOCK_READ_WITH_RETRY(lock, readStuff) { uint32_t aemu_seqlock_curr_seq; do { \
315 aemu_seqlock_curr_seq = (lock)->beginRead(); \
316 readStuff; \
317 } while ((lock)->shouldRetryRead(aemu_seqlock_curr_seq)); }
318
319 private:
320 std::atomic<uint32_t> mSeq { 0 }; // The sequence number
321 Lock mWriteLock; // Just use a normal mutex to protect writes
322 };
323
324 } // namespace base
325 } // namespace android
326