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