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