• 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 
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()124 void ANGLE_CONSTRUCTOR AllocateGlobalMutex()
125 {
126     ASSERT(g_MutexPtr == nullptr);
127     g_MutexPtr = new priv::GlobalMutex();
128 }
129 
DeallocateGlobalMutex()130 void 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()164 ScopedGlobalMutexLock::ScopedGlobalMutexLock()
165 {
166     g_MutexPtr->lock();
167 }
168 
~ScopedGlobalMutexLock()169 ScopedGlobalMutexLock::~ScopedGlobalMutexLock()
170 {
171     g_MutexPtr->unlock();
172 }
173 #else
ScopedGlobalMutexLock()174 ScopedGlobalMutexLock::ScopedGlobalMutexLock() : mMutex(*GetGlobalMutex())
175 {
176     mMutex.lock();
177 }
178 
~ScopedGlobalMutexLock()179 ScopedGlobalMutexLock::~ScopedGlobalMutexLock()
180 {
181     mMutex.unlock();
182 }
183 #endif
184 
185 // ScopedOptionalGlobalMutexLock implementation.
ScopedOptionalGlobalMutexLock(bool enabled)186 ScopedOptionalGlobalMutexLock::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()203 ScopedOptionalGlobalMutexLock::~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()217 void AllocateGlobalMutex()
218 {
219     (void)AllocateGlobalMutexImpl();
220 }
221 
DeallocateGlobalMutex()222 void 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