• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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()124 void 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()132 void 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()176 ScopedGlobalMutexLock<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()193 ScopedGlobalMutexLock<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()210 ScopedGlobalMutexLock<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()229 ScopedGlobalMutexLock<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)240 ScopedOptionalGlobalMutexLock::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()257 ScopedOptionalGlobalMutexLock::~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()271 void AllocateGlobalMutex()
272 {
273     (void)priv::AllocateGlobalMutexImpl(&priv::g_Mutex);
274     (void)priv::AllocateGlobalMutexImpl(&priv::g_EGLSyncMutex);
275 }
276 
DeallocateGlobalMutexImpl(std::atomic<priv::GlobalMutex * > * globalMutex)277 void 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()290 void DeallocateGlobalMutex()
291 {
292     DeallocateGlobalMutexImpl(&priv::g_Mutex);
293     DeallocateGlobalMutexImpl(&priv::g_EGLSyncMutex);
294 }
295 #endif
296 
297 }  // namespace egl
298