xref: /aosp_15_r20/external/angle/src/libANGLE/GlobalMutex.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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()123 void 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()131 void 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()175 ScopedGlobalMutexLock<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()192 ScopedGlobalMutexLock<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()209 ScopedGlobalMutexLock<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()228 ScopedGlobalMutexLock<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)239 ScopedOptionalGlobalMutexLock::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()256 ScopedOptionalGlobalMutexLock::~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()270 void AllocateGlobalMutex()
271 {
272     (void)priv::AllocateGlobalMutexImpl(&priv::g_Mutex);
273     (void)priv::AllocateGlobalMutexImpl(&priv::g_EGLSyncMutex);
274 }
275 
DeallocateGlobalMutexImpl(std::atomic<priv::GlobalMutex * > * globalMutex)276 void 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()289 void DeallocateGlobalMutex()
290 {
291     DeallocateGlobalMutexImpl(&priv::g_Mutex);
292     DeallocateGlobalMutexImpl(&priv::g_EGLSyncMutex);
293 }
294 #endif
295 
296 }  // namespace egl
297