• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "android/base/Compiler.h"
18 
19 #ifdef _WIN32
20 #define WIN32_LEAN_AND_MEAN 1
21 #include <windows.h>
22 #else
23 #include <pthread.h>
24 #endif
25 
26 #include <assert.h>
27 
28 #define AEMU_DEBUG 0
29 
30 #if AEMU_DEBUG
31 #define AEMU_IF_DEBUG(x) x
32 #else
33 #define AEMU_IF_DEBUG(x)
34 #endif
35 
36 namespace android {
37 namespace base {
38 namespace guest {
39 
40 template <class Lockable>
41 class AutoLock;
42 
43 class AutoWriteLock;
44 class AutoReadLock;
45 
46 // A wrapper class for mutexes only suitable for using in static context,
47 // where it's OK to leak the underlying system object.
48 // Use Lock / RecursiveLock for scoped or member locks.
49 template <bool IsRecursive>
50 class StaticLock;
51 
52 template <>
53 class StaticLock<false> {
54 public:
55     using AutoLock = android::base::guest::AutoLock<StaticLock>;
56 
57     constexpr StaticLock() = default;
58 
59     // Acquire the lock.
lock()60     void lock() {
61 #ifdef _WIN32
62         ::AcquireSRWLockExclusive(&mLock);
63 #else
64         ::pthread_mutex_lock(&mLock);
65 #endif
66         AEMU_IF_DEBUG(mIsLocked = true;)
67     }
68 
tryLock()69     bool tryLock() {
70         bool ret = false;
71 #ifdef _WIN32
72         ret = ::TryAcquireSRWLockExclusive(&mLock);
73 #else
74         ret = ::pthread_mutex_trylock(&mLock) == 0;
75 #endif
76         AEMU_IF_DEBUG(mIsLocked = ret;)
77         return ret;
78     }
79 
AEMU_IF_DEBUG(bool isLocked ()const{ return mIsLocked; })80     AEMU_IF_DEBUG(bool isLocked() const { return mIsLocked; })
81 
82     // Release the lock.
83     void unlock() {
84         AEMU_IF_DEBUG(mIsLocked = false;)
85 #ifdef _WIN32
86         ::ReleaseSRWLockExclusive(&mLock);
87 #else
88         ::pthread_mutex_unlock(&mLock);
89 #endif
90     }
91 
92 protected:
93     friend class ConditionVariable;
94 
95 #ifdef _WIN32
96     // Benchmarks show that on Windows SRWLOCK performs a little bit better than
97     // CRITICAL_SECTION for uncontended mode and much better in case of
98     // contention.
99     SRWLOCK mLock = SRWLOCK_INIT;
100 #else
101     pthread_mutex_t mLock = PTHREAD_MUTEX_INITIALIZER;
102 #endif
103     // Both POSIX threads and WinAPI don't allow move (undefined behavior).
104     DISALLOW_COPY_ASSIGN_AND_MOVE(StaticLock);
105 
106     AEMU_IF_DEBUG(bool mIsLocked = false;)
107 };
108 
109 template <>
110 class StaticLock<true> {
111 public:
112     using AutoLock = android::base::guest::AutoLock<StaticLock>;
113 
StaticLock()114     StaticLock() {
115 #ifdef _WIN32
116         ::InitializeCriticalSectionAndSpinCount(&mLock, 0x400);
117 #else
118         pthread_mutexattr_t attr;
119         pthread_mutexattr_init(&attr);
120         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
121         pthread_mutex_init(&mLock, &attr);
122 #endif
123     }
124 
125     // Acquire the lock.
lock()126     void lock() {
127 #ifdef _WIN32
128         ::EnterCriticalSection(&mLock);
129 #else
130         ::pthread_mutex_lock(&mLock);
131 #endif
132         AEMU_IF_DEBUG(mIsLocked = true;)
133     }
134 
tryLock()135     bool tryLock() {
136         bool ret = false;
137 #ifdef _WIN32
138         ret = ::TryEnterCriticalSection(&mLock);
139 #else
140         ret = ::pthread_mutex_trylock(&mLock) == 0;
141 #endif
142         AEMU_IF_DEBUG(mIsLocked = ret;)
143         return ret;
144     }
145 
AEMU_IF_DEBUG(bool isLocked ()const{ return mIsLocked; })146     AEMU_IF_DEBUG(bool isLocked() const { return mIsLocked; })
147 
148     // Release the lock.
149     void unlock() {
150         AEMU_IF_DEBUG(mIsLocked = false;)
151 #ifdef _WIN32
152         ::LeaveCriticalSection(&mLock);
153 #else
154         ::pthread_mutex_unlock(&mLock);
155 #endif
156     }
157 
158 protected:
159     friend class ConditionVariable;
160 
161 #ifdef _WIN32
162     // We use CRITICAL_SECTION since it always allow recursive access.
163     CRITICAL_SECTION mLock;
164 #else
165     pthread_mutex_t mLock = PTHREAD_MUTEX_INITIALIZER;
166 #endif
167     // Both POSIX threads and WinAPI don't allow move (undefined behavior).
168     DISALLOW_COPY_ASSIGN_AND_MOVE(StaticLock);
169 
170     AEMU_IF_DEBUG(bool mIsLocked = false;)
171 };
172 
173 // Simple wrapper class for mutexes used in non-static context.
174 class Lock : public StaticLock<false> {
175 public:
176     using StaticLock::AutoLock;
177 
178     constexpr Lock() = default;
179 #ifndef _WIN32
180     // The only difference is that POSIX requires a deallocation function call
181     // for its mutexes.
~Lock()182     ~Lock() { ::pthread_mutex_destroy(&mLock); }
183 #endif
184 };
185 
186 // Simple wrapper class for mutexes used in non-static context.
187 class RecursiveLock : public StaticLock<true> {
188 public:
189     using StaticLock::AutoLock;
190 
191     RecursiveLock() = default;
192 
~RecursiveLock()193     ~RecursiveLock() {
194 #ifdef _WIN32
195         ::DeleteCriticalSection(&mLock);
196 #else
197         ::pthread_mutex_destroy(&mLock);
198 #endif
199     }
200 };
201 
202 class ReadWriteLock {
203 public:
204     using AutoWriteLock = android::base::guest::AutoWriteLock;
205     using AutoReadLock = android::base::guest::AutoReadLock;
206 
207 #ifdef _WIN32
208     constexpr ReadWriteLock() = default;
209     ~ReadWriteLock() = default;
lockRead()210     void lockRead() { ::AcquireSRWLockShared(&mLock); }
unlockRead()211     void unlockRead() { ::ReleaseSRWLockShared(&mLock); }
lockWrite()212     void lockWrite() { ::AcquireSRWLockExclusive(&mLock); }
unlockWrite()213     void unlockWrite() { ::ReleaseSRWLockExclusive(&mLock); }
214 
215 private:
216     SRWLOCK mLock = SRWLOCK_INIT;
217 #else   // !_WIN32
ReadWriteLock()218     ReadWriteLock() { ::pthread_rwlock_init(&mLock, NULL); }
~ReadWriteLock()219     ~ReadWriteLock() { ::pthread_rwlock_destroy(&mLock); }
lockRead()220     void lockRead() { ::pthread_rwlock_rdlock(&mLock); }
unlockRead()221     void unlockRead() { ::pthread_rwlock_unlock(&mLock); }
lockWrite()222     void lockWrite() { ::pthread_rwlock_wrlock(&mLock); }
unlockWrite()223     void unlockWrite() { ::pthread_rwlock_unlock(&mLock); }
224 
225 private:
226     pthread_rwlock_t mLock;
227 #endif  // !_WIN32
228 
229     friend class ConditionVariable;
230     DISALLOW_COPY_ASSIGN_AND_MOVE(ReadWriteLock);
231 };
232 
233 // Helper class to lock / unlock a mutex automatically on scope
234 // entry and exit.
235 // NB: not thread-safe (as opposed to the Lock class)
236 template <class Lockable>
237 class AutoLock {
238 public:
AutoLock(Lockable & lock)239     AutoLock(Lockable& lock) : mLock(lock) { mLock.lock(); }
240 
AutoLock(AutoLock<Lockable> && other)241     AutoLock(AutoLock<Lockable>&& other) : mLock(other.mLock), mLocked(other.mLocked) {
242         other.mLocked = false;
243     }
244 
lock()245     void lock() {
246         assert(!mLocked);
247         mLock.lock();
248         mLocked = true;
249     }
250 
unlock()251     void unlock() {
252         assert(mLocked);
253         mLock.unlock();
254         mLocked = false;
255     }
256 
isLocked()257     bool isLocked() const { return mLocked; }
258 
~AutoLock()259     ~AutoLock() {
260         if (mLocked) {
261             mLock.unlock();
262         }
263     }
264 
265 private:
266     Lockable& mLock;
267     bool mLocked = true;
268 
269     friend class ConditionVariable;
270     // Don't allow move because this class has a non-movable object.
271     DISALLOW_COPY_AND_ASSIGN(AutoLock);
272 };
273 
274 class AutoWriteLock {
275 public:
AutoWriteLock(ReadWriteLock & lock)276     AutoWriteLock(ReadWriteLock& lock) : mLock(lock) { mLock.lockWrite(); }
277 
lockWrite()278     void lockWrite() {
279         assert(!mWriteLocked);
280         mLock.lockWrite();
281         mWriteLocked = true;
282     }
283 
unlockWrite()284     void unlockWrite() {
285         assert(mWriteLocked);
286         mLock.unlockWrite();
287         mWriteLocked = false;
288     }
289 
~AutoWriteLock()290     ~AutoWriteLock() {
291         if (mWriteLocked) {
292             mLock.unlockWrite();
293         }
294     }
295 
296 private:
297     ReadWriteLock& mLock;
298     bool mWriteLocked = true;
299     // This class has a non-movable object.
300     DISALLOW_COPY_ASSIGN_AND_MOVE(AutoWriteLock);
301 };
302 
303 class AutoReadLock {
304 public:
AutoReadLock(ReadWriteLock & lock)305     AutoReadLock(ReadWriteLock& lock) : mLock(lock) { mLock.lockRead(); }
306 
lockRead()307     void lockRead() {
308         assert(!mReadLocked);
309         mLock.lockRead();
310         mReadLocked = true;
311     }
312 
unlockRead()313     void unlockRead() {
314         assert(mReadLocked);
315         mLock.unlockRead();
316         mReadLocked = false;
317     }
318 
~AutoReadLock()319     ~AutoReadLock() {
320         if (mReadLocked) {
321             mLock.unlockRead();
322         }
323     }
324 
325 private:
326     ReadWriteLock& mLock;
327     bool mReadLocked = true;
328     // This class has a non-movable object.
329     DISALLOW_COPY_ASSIGN_AND_MOVE(AutoReadLock);
330 };
331 
332 }  // namespace guest
333 }  // namespace base
334 }  // namespace android
335