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 priv 113 114 namespace 115 { 116 #if defined(ANGLE_ENABLE_GLOBAL_MUTEX_LOAD_TIME_ALLOCATE) 117 # if !ANGLE_HAS_ATTRIBUTE_CONSTRUCTOR || !ANGLE_HAS_ATTRIBUTE_DESTRUCTOR 118 # error \ 119 "'angle_enable_global_mutex_load_time_allocate' " \ 120 "requires constructor/destructor compiler atributes." 121 # endif 122 priv::GlobalMutex *g_MutexPtr = nullptr; 123 AllocateGlobalMutex()124void ANGLE_CONSTRUCTOR AllocateGlobalMutex() 125 { 126 ASSERT(g_MutexPtr == nullptr); 127 g_MutexPtr = new priv::GlobalMutex(); 128 } 129 DeallocateGlobalMutex()130void ANGLE_DESTRUCTOR DeallocateGlobalMutex() 131 { 132 SafeDelete(g_MutexPtr); 133 } 134 135 #else 136 ANGLE_REQUIRE_CONSTANT_INIT std::atomic<priv::GlobalMutex *> g_Mutex(nullptr); 137 static_assert(std::is_trivially_destructible<decltype(g_Mutex)>::value, 138 "global mutex is not trivially destructible"); 139 140 priv::GlobalMutex *AllocateGlobalMutexImpl() 141 { 142 priv::GlobalMutex *currentMutex = nullptr; 143 std::unique_ptr<priv::GlobalMutex> newMutex(new priv::GlobalMutex()); 144 do 145 { 146 if (g_Mutex.compare_exchange_weak(currentMutex, newMutex.get())) 147 { 148 return newMutex.release(); 149 } 150 } while (currentMutex == nullptr); 151 return currentMutex; 152 } 153 154 priv::GlobalMutex *GetGlobalMutex() 155 { 156 priv::GlobalMutex *mutex = g_Mutex.load(); 157 return mutex != nullptr ? mutex : AllocateGlobalMutexImpl(); 158 } 159 #endif 160 } // anonymous namespace 161 162 // ScopedGlobalMutexLock implementation. 163 #if defined(ANGLE_ENABLE_GLOBAL_MUTEX_LOAD_TIME_ALLOCATE) ScopedGlobalMutexLock()164ScopedGlobalMutexLock::ScopedGlobalMutexLock() 165 { 166 g_MutexPtr->lock(); 167 } 168 ~ScopedGlobalMutexLock()169ScopedGlobalMutexLock::~ScopedGlobalMutexLock() 170 { 171 g_MutexPtr->unlock(); 172 } 173 #else ScopedGlobalMutexLock()174ScopedGlobalMutexLock::ScopedGlobalMutexLock() : mMutex(*GetGlobalMutex()) 175 { 176 mMutex.lock(); 177 } 178 ~ScopedGlobalMutexLock()179ScopedGlobalMutexLock::~ScopedGlobalMutexLock() 180 { 181 mMutex.unlock(); 182 } 183 #endif 184 185 // ScopedOptionalGlobalMutexLock implementation. ScopedOptionalGlobalMutexLock(bool enabled)186ScopedOptionalGlobalMutexLock::ScopedOptionalGlobalMutexLock(bool enabled) 187 { 188 if (enabled) 189 { 190 #if defined(ANGLE_ENABLE_GLOBAL_MUTEX_LOAD_TIME_ALLOCATE) 191 mMutex = g_MutexPtr; 192 #else 193 mMutex = GetGlobalMutex(); 194 #endif 195 mMutex->lock(); 196 } 197 else 198 { 199 mMutex = nullptr; 200 } 201 } 202 ~ScopedOptionalGlobalMutexLock()203ScopedOptionalGlobalMutexLock::~ScopedOptionalGlobalMutexLock() 204 { 205 if (mMutex != nullptr) 206 { 207 mMutex->unlock(); 208 } 209 } 210 211 // Global functions. 212 #if defined(ANGLE_PLATFORM_WINDOWS) && !defined(ANGLE_STATIC) 213 # if defined(ANGLE_ENABLE_GLOBAL_MUTEX_LOAD_TIME_ALLOCATE) 214 # error "'angle_enable_global_mutex_load_time_allocate' is not supported in Windows DLL." 215 # endif 216 AllocateGlobalMutex()217void AllocateGlobalMutex() 218 { 219 (void)AllocateGlobalMutexImpl(); 220 } 221 DeallocateGlobalMutex()222void DeallocateGlobalMutex() 223 { 224 priv::GlobalMutex *mutex = g_Mutex.exchange(nullptr); 225 if (mutex != nullptr) 226 { 227 { 228 // Wait for the mutex to become released by other threads before deleting. 229 std::lock_guard<priv::GlobalMutex> lock(*mutex); 230 } 231 delete mutex; 232 } 233 } 234 #endif 235 236 } // namespace egl 237