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 class AutoLock;
41 class AutoWriteLock;
42 class AutoReadLock;
43 
44 // A wrapper class for mutexes only suitable for using in static context,
45 // where it's OK to leak the underlying system object. Use Lock for scoped or
46 // member locks.
47 class StaticLock {
48 public:
49     using AutoLock = android::base::guest::AutoLock;
50 
51     constexpr StaticLock() = default;
52 
53     // Acquire the lock.
lock()54     void lock() {
55 #ifdef _WIN32
56         ::AcquireSRWLockExclusive(&mLock);
57 #else
58         ::pthread_mutex_lock(&mLock);
59 #endif
60         AEMU_IF_DEBUG(mIsLocked = true;)
61     }
62 
tryLock()63     bool tryLock() {
64         bool ret = false;
65 #ifdef _WIN32
66         ret = ::TryAcquireSRWLockExclusive(&mLock);
67 #else
68         ret = ::pthread_mutex_trylock(&mLock) == 0;
69 #endif
70         AEMU_IF_DEBUG(mIsLocked = ret;)
71         return ret;
72     }
73 
AEMU_IF_DEBUG(bool isLocked ()const{ return mIsLocked; })74     AEMU_IF_DEBUG(bool isLocked() const { return mIsLocked; })
75 
76     // Release the lock.
77     void unlock() {
78         AEMU_IF_DEBUG(mIsLocked = false;)
79 #ifdef _WIN32
80         ::ReleaseSRWLockExclusive(&mLock);
81 #else
82         ::pthread_mutex_unlock(&mLock);
83 #endif
84     }
85 
86 protected:
87     friend class ConditionVariable;
88 
89 #ifdef _WIN32
90     // Benchmarks show that on Windows SRWLOCK performs a little bit better than
91     // CRITICAL_SECTION for uncontended mode and much better in case of
92     // contention.
93     SRWLOCK mLock = SRWLOCK_INIT;
94 #else
95     pthread_mutex_t mLock = PTHREAD_MUTEX_INITIALIZER;
96 #endif
97     // Both POSIX threads and WinAPI don't allow move (undefined behavior).
98     DISALLOW_COPY_ASSIGN_AND_MOVE(StaticLock);
99 
100     AEMU_IF_DEBUG(bool mIsLocked = false;)
101 };
102 
103 // Simple wrapper class for mutexes used in non-static context.
104 class Lock : public StaticLock {
105 public:
106     using StaticLock::AutoLock;
107 
108     constexpr Lock() = default;
109 #ifndef _WIN32
110     // The only difference is that POSIX requires a deallocation function call
111     // for its mutexes.
~Lock()112     ~Lock() { ::pthread_mutex_destroy(&mLock); }
113 #endif
114 };
115 
116 class ReadWriteLock {
117 public:
118     using AutoWriteLock = android::base::guest::AutoWriteLock;
119     using AutoReadLock = android::base::guest::AutoReadLock;
120 
121 #ifdef _WIN32
122     constexpr ReadWriteLock() = default;
123     ~ReadWriteLock() = default;
lockRead()124     void lockRead() { ::AcquireSRWLockShared(&mLock); }
unlockRead()125     void unlockRead() { ::ReleaseSRWLockShared(&mLock); }
lockWrite()126     void lockWrite() { ::AcquireSRWLockExclusive(&mLock); }
unlockWrite()127     void unlockWrite() { ::ReleaseSRWLockExclusive(&mLock); }
128 
129 private:
130     SRWLOCK mLock = SRWLOCK_INIT;
131 #else   // !_WIN32
ReadWriteLock()132     ReadWriteLock() { ::pthread_rwlock_init(&mLock, NULL); }
~ReadWriteLock()133     ~ReadWriteLock() { ::pthread_rwlock_destroy(&mLock); }
lockRead()134     void lockRead() { ::pthread_rwlock_rdlock(&mLock); }
unlockRead()135     void unlockRead() { ::pthread_rwlock_unlock(&mLock); }
lockWrite()136     void lockWrite() { ::pthread_rwlock_wrlock(&mLock); }
unlockWrite()137     void unlockWrite() { ::pthread_rwlock_unlock(&mLock); }
138 
139 private:
140     pthread_rwlock_t mLock;
141 #endif  // !_WIN32
142 
143     friend class ConditionVariable;
144     DISALLOW_COPY_ASSIGN_AND_MOVE(ReadWriteLock);
145 };
146 
147 // Helper class to lock / unlock a mutex automatically on scope
148 // entry and exit.
149 // NB: not thread-safe (as opposed to the Lock class)
150 class AutoLock {
151 public:
AutoLock(StaticLock & lock)152     AutoLock(StaticLock& lock) : mLock(lock) { mLock.lock(); }
153 
AutoLock(AutoLock && other)154     AutoLock(AutoLock&& other) : mLock(other.mLock), mLocked(other.mLocked) {
155         other.mLocked = false;
156     }
157 
lock()158     void lock() {
159         assert(!mLocked);
160         mLock.lock();
161         mLocked = true;
162     }
163 
unlock()164     void unlock() {
165         assert(mLocked);
166         mLock.unlock();
167         mLocked = false;
168     }
169 
isLocked()170     bool isLocked() const { return mLocked; }
171 
~AutoLock()172     ~AutoLock() {
173         if (mLocked) {
174             mLock.unlock();
175         }
176     }
177 
178 private:
179     StaticLock& mLock;
180     bool mLocked = true;
181 
182     friend class ConditionVariable;
183     // Don't allow move because this class has a non-movable object.
184     DISALLOW_COPY_AND_ASSIGN(AutoLock);
185 };
186 
187 class AutoWriteLock {
188 public:
AutoWriteLock(ReadWriteLock & lock)189     AutoWriteLock(ReadWriteLock& lock) : mLock(lock) { mLock.lockWrite(); }
190 
lockWrite()191     void lockWrite() {
192         assert(!mWriteLocked);
193         mLock.lockWrite();
194         mWriteLocked = true;
195     }
196 
unlockWrite()197     void unlockWrite() {
198         assert(mWriteLocked);
199         mLock.unlockWrite();
200         mWriteLocked = false;
201     }
202 
~AutoWriteLock()203     ~AutoWriteLock() {
204         if (mWriteLocked) {
205             mLock.unlockWrite();
206         }
207     }
208 
209 private:
210     ReadWriteLock& mLock;
211     bool mWriteLocked = true;
212     // This class has a non-movable object.
213     DISALLOW_COPY_ASSIGN_AND_MOVE(AutoWriteLock);
214 };
215 
216 class AutoReadLock {
217 public:
AutoReadLock(ReadWriteLock & lock)218     AutoReadLock(ReadWriteLock& lock) : mLock(lock) { mLock.lockRead(); }
219 
lockRead()220     void lockRead() {
221         assert(!mReadLocked);
222         mLock.lockRead();
223         mReadLocked = true;
224     }
225 
unlockRead()226     void unlockRead() {
227         assert(mReadLocked);
228         mLock.unlockRead();
229         mReadLocked = false;
230     }
231 
~AutoReadLock()232     ~AutoReadLock() {
233         if (mReadLocked) {
234             mLock.unlockRead();
235         }
236     }
237 
238 private:
239     ReadWriteLock& mLock;
240     bool mReadLocked = true;
241     // This class has a non-movable object.
242     DISALLOW_COPY_ASSIGN_AND_MOVE(AutoReadLock);
243 };
244 
245 }  // namespace guest
246 }  // namespace base
247 }  // namespace android
248