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