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