1 // 2 // Copyright 2023 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 // GlobalMutex.cpp: Defines Global Mutex and utilities. 7 8 #include "libANGLE/GlobalMutex.h" 9 10 #include <atomic> 11 12 #include "common/debug.h" 13 #include "common/system_utils.h" 14 15 namespace egl 16 { 17 namespace priv 18 { 19 using GlobalMutexType = std::mutex; 20 21 #if !defined(ANGLE_ENABLE_ASSERTS) && !defined(ANGLE_ENABLE_GLOBAL_MUTEX_RECURSION) 22 // Default version. 23 class GlobalMutex final : angle::NonCopyable 24 { 25 public: lock()26 ANGLE_INLINE void lock() { mMutex.lock(); } unlock()27 ANGLE_INLINE void unlock() { mMutex.unlock(); } 28 29 protected: 30 GlobalMutexType mMutex; 31 }; 32 #endif 33 34 #if defined(ANGLE_ENABLE_ASSERTS) && !defined(ANGLE_ENABLE_GLOBAL_MUTEX_RECURSION) 35 // Debug version. 36 class GlobalMutex final : angle::NonCopyable 37 { 38 public: lock()39 ANGLE_INLINE void lock() 40 { 41 const angle::ThreadId threadId = angle::GetCurrentThreadId(); 42 ASSERT(getOwnerThreadId() != threadId); 43 mMutex.lock(); 44 ASSERT(getOwnerThreadId() == angle::InvalidThreadId()); 45 mOwnerThreadId.store(threadId, std::memory_order_relaxed); 46 } 47 unlock()48 ANGLE_INLINE void unlock() 49 { 50 ASSERT(getOwnerThreadId() == angle::GetCurrentThreadId()); 51 mOwnerThreadId.store(angle::InvalidThreadId(), std::memory_order_relaxed); 52 mMutex.unlock(); 53 } 54 55 private: getOwnerThreadId() const56 ANGLE_INLINE angle::ThreadId getOwnerThreadId() const 57 { 58 return mOwnerThreadId.load(std::memory_order_relaxed); 59 } 60 61 GlobalMutexType mMutex; 62 std::atomic<angle::ThreadId> mOwnerThreadId{angle::InvalidThreadId()}; 63 }; 64 #endif // defined(ANGLE_ENABLE_ASSERTS) && !defined(ANGLE_ENABLE_GLOBAL_MUTEX_RECURSION) 65 66 #if defined(ANGLE_ENABLE_GLOBAL_MUTEX_RECURSION) 67 // Recursive version. 68 class GlobalMutex final : angle::NonCopyable 69 { 70 public: lock()71 ANGLE_INLINE void lock() 72 { 73 const angle::ThreadId threadId = angle::GetCurrentThreadId(); 74 if (ANGLE_UNLIKELY(!mMutex.try_lock())) 75 { 76 if (ANGLE_UNLIKELY(getOwnerThreadId() == threadId)) 77 { 78 ASSERT(mLockLevel > 0); 79 ++mLockLevel; 80 return; 81 } 82 mMutex.lock(); 83 } 84 ASSERT(getOwnerThreadId() == angle::InvalidThreadId()); 85 ASSERT(mLockLevel == 0); 86 mOwnerThreadId.store(threadId, std::memory_order_relaxed); 87 mLockLevel = 1; 88 } 89 unlock()90 ANGLE_INLINE void unlock() 91 { 92 ASSERT(getOwnerThreadId() == angle::GetCurrentThreadId()); 93 ASSERT(mLockLevel > 0); 94 if (ANGLE_LIKELY(--mLockLevel == 0)) 95 { 96 mOwnerThreadId.store(angle::InvalidThreadId(), std::memory_order_relaxed); 97 mMutex.unlock(); 98 } 99 } 100 101 private: getOwnerThreadId() const102 ANGLE_INLINE angle::ThreadId getOwnerThreadId() const 103 { 104 return mOwnerThreadId.load(std::memory_order_relaxed); 105 } 106 107 GlobalMutexType mMutex; 108 std::atomic<angle::ThreadId> mOwnerThreadId{angle::InvalidThreadId()}; 109 uint32_t mLockLevel = 0; 110 }; 111 #endif // defined(ANGLE_ENABLE_GLOBAL_MUTEX_RECURSION) 112 namespace 113 { 114 #if defined(ANGLE_ENABLE_GLOBAL_MUTEX_LOAD_TIME_ALLOCATE) 115 # if !ANGLE_HAS_ATTRIBUTE_CONSTRUCTOR || !ANGLE_HAS_ATTRIBUTE_DESTRUCTOR 116 # error \ 117 "'angle_enable_global_mutex_load_time_allocate' " \ 118 "requires constructor/destructor compiler atributes." 119 # endif 120 GlobalMutex *g_MutexPtr = nullptr; 121 GlobalMutex *g_EGLSyncMutexPtr = nullptr; 122 AllocateGlobalMutex()123void ANGLE_CONSTRUCTOR AllocateGlobalMutex() 124 { 125 ASSERT(g_MutexPtr == nullptr); 126 g_MutexPtr = new GlobalMutex(); 127 ASSERT(g_EGLSyncMutexPtr == nullptr); 128 g_EGLSyncMutexPtr = new GlobalMutex(); 129 } 130 DeallocateGlobalMutex()131void ANGLE_DESTRUCTOR DeallocateGlobalMutex() 132 { 133 SafeDelete(g_MutexPtr); 134 SafeDelete(g_EGLSyncMutexPtr); 135 } 136 #else 137 ANGLE_REQUIRE_CONSTANT_INIT std::atomic<GlobalMutex *> g_Mutex(nullptr); 138 ANGLE_REQUIRE_CONSTANT_INIT std::atomic<GlobalMutex *> g_EGLSyncMutex(nullptr); 139 static_assert(std::is_trivially_destructible<decltype(g_Mutex)>::value, 140 "global mutex is not trivially destructible"); 141 static_assert(std::is_trivially_destructible<decltype(g_EGLSyncMutex)>::value, 142 "global EGL Sync mutex is not trivially destructible"); 143 144 GlobalMutex *AllocateGlobalMutexImpl(std::atomic<GlobalMutex *> *globalMutex) 145 { 146 GlobalMutex *currentMutex = nullptr; 147 std::unique_ptr<GlobalMutex> newMutex(new GlobalMutex()); 148 do 149 { 150 if (globalMutex->compare_exchange_weak(currentMutex, newMutex.get())) 151 { 152 return newMutex.release(); 153 } 154 } while (currentMutex == nullptr); 155 return currentMutex; 156 } 157 158 GlobalMutex *GetGlobalMutex() 159 { 160 GlobalMutex *mutex = g_Mutex.load(); 161 return mutex != nullptr ? mutex : AllocateGlobalMutexImpl(&g_Mutex); 162 } 163 164 GlobalMutex *GetGlobalEGLSyncObjectMutex() 165 { 166 GlobalMutex *mutex = g_EGLSyncMutex.load(); 167 return mutex != nullptr ? mutex : AllocateGlobalMutexImpl(&g_EGLSyncMutex); 168 } 169 #endif 170 } // anonymous namespace 171 172 // ScopedGlobalMutexLock implementation. 173 #if defined(ANGLE_ENABLE_GLOBAL_MUTEX_LOAD_TIME_ALLOCATE) 174 template <GlobalMutexChoice mutexChoice> ScopedGlobalMutexLock()175ScopedGlobalMutexLock<mutexChoice>::ScopedGlobalMutexLock() 176 { 177 switch (mutexChoice) 178 { 179 case GlobalMutexChoice::EGL: 180 g_MutexPtr->lock(); 181 break; 182 case GlobalMutexChoice::Sync: 183 g_EGLSyncMutexPtr->lock(); 184 break; 185 default: 186 UNREACHABLE(); 187 break; 188 } 189 } 190 191 template <GlobalMutexChoice mutexChoice> ~ScopedGlobalMutexLock()192ScopedGlobalMutexLock<mutexChoice>::~ScopedGlobalMutexLock() 193 { 194 switch (mutexChoice) 195 { 196 case GlobalMutexChoice::EGL: 197 g_MutexPtr->unlock(); 198 break; 199 case GlobalMutexChoice::Sync: 200 g_EGLSyncMutexPtr->unlock(); 201 break; 202 default: 203 UNREACHABLE(); 204 break; 205 } 206 } 207 #else 208 template <GlobalMutexChoice mutexChoice> ScopedGlobalMutexLock()209ScopedGlobalMutexLock<mutexChoice>::ScopedGlobalMutexLock() 210 { 211 switch (mutexChoice) 212 { 213 case GlobalMutexChoice::EGL: 214 mMutex = GetGlobalMutex(); 215 break; 216 case GlobalMutexChoice::Sync: 217 mMutex = GetGlobalEGLSyncObjectMutex(); 218 break; 219 default: 220 UNREACHABLE(); 221 break; 222 } 223 224 mMutex->lock(); 225 } 226 227 template <GlobalMutexChoice mutexChoice> ~ScopedGlobalMutexLock()228ScopedGlobalMutexLock<mutexChoice>::~ScopedGlobalMutexLock() 229 { 230 mMutex->unlock(); 231 } 232 #endif 233 234 template class ScopedGlobalMutexLock<GlobalMutexChoice::EGL>; 235 template class ScopedGlobalMutexLock<GlobalMutexChoice::Sync>; 236 } // namespace priv 237 238 // ScopedOptionalGlobalMutexLock implementation. ScopedOptionalGlobalMutexLock(bool enabled)239ScopedOptionalGlobalMutexLock::ScopedOptionalGlobalMutexLock(bool enabled) 240 { 241 if (enabled) 242 { 243 #if defined(ANGLE_ENABLE_GLOBAL_MUTEX_LOAD_TIME_ALLOCATE) 244 mMutex = priv::g_MutexPtr; 245 #else 246 mMutex = priv::GetGlobalMutex(); 247 #endif 248 mMutex->lock(); 249 } 250 else 251 { 252 mMutex = nullptr; 253 } 254 } 255 ~ScopedOptionalGlobalMutexLock()256ScopedOptionalGlobalMutexLock::~ScopedOptionalGlobalMutexLock() 257 { 258 if (mMutex != nullptr) 259 { 260 mMutex->unlock(); 261 } 262 } 263 264 // Global functions. 265 #if defined(ANGLE_PLATFORM_WINDOWS) && !defined(ANGLE_STATIC) 266 # if defined(ANGLE_ENABLE_GLOBAL_MUTEX_LOAD_TIME_ALLOCATE) 267 # error "'angle_enable_global_mutex_load_time_allocate' is not supported in Windows DLL." 268 # endif 269 AllocateGlobalMutex()270void AllocateGlobalMutex() 271 { 272 (void)priv::AllocateGlobalMutexImpl(&priv::g_Mutex); 273 (void)priv::AllocateGlobalMutexImpl(&priv::g_EGLSyncMutex); 274 } 275 DeallocateGlobalMutexImpl(std::atomic<priv::GlobalMutex * > * globalMutex)276void DeallocateGlobalMutexImpl(std::atomic<priv::GlobalMutex *> *globalMutex) 277 { 278 priv::GlobalMutex *mutex = globalMutex->exchange(nullptr); 279 if (mutex != nullptr) 280 { 281 { 282 // Wait for the mutex to become released by other threads before deleting. 283 std::lock_guard<priv::GlobalMutex> lock(*mutex); 284 } 285 delete mutex; 286 } 287 } 288 DeallocateGlobalMutex()289void DeallocateGlobalMutex() 290 { 291 DeallocateGlobalMutexImpl(&priv::g_Mutex); 292 DeallocateGlobalMutexImpl(&priv::g_EGLSyncMutex); 293 } 294 #endif 295 296 } // namespace egl 297