1 //
2 // Copyright 2014 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
7 // global_state.cpp : Implements functions for querying the thread-local GL and EGL state.
8
9 #include "libGLESv2/global_state.h"
10
11 #include "common/debug.h"
12 #include "common/platform.h"
13 #include "common/system_utils.h"
14 #include "libANGLE/ErrorStrings.h"
15 #include "libANGLE/Thread.h"
16 #include "libGLESv2/resource.h"
17
18 #include <atomic>
19 #if defined(ANGLE_PLATFORM_APPLE)
20 # include <dispatch/dispatch.h>
21 #endif
22 namespace egl
23 {
24 namespace
25 {
26 ANGLE_REQUIRE_CONSTANT_INIT std::atomic<angle::GlobalMutex *> g_Mutex(nullptr);
27 static_assert(std::is_trivially_destructible<decltype(g_Mutex)>::value,
28 "global mutex is not trivially destructible");
29 ANGLE_REQUIRE_CONSTANT_INIT std::atomic<angle::GlobalMutex *> g_SurfaceMutex(nullptr);
30 static_assert(std::is_trivially_destructible<decltype(g_SurfaceMutex)>::value,
31 "global mutex is not trivially destructible");
32
33 ANGLE_REQUIRE_CONSTANT_INIT gl::Context *g_LastContext(nullptr);
34 static_assert(std::is_trivially_destructible<decltype(g_LastContext)>::value,
35 "global last context is not trivially destructible");
36
SetContextToAndroidOpenGLTLSSlot(gl::Context * value)37 void SetContextToAndroidOpenGLTLSSlot(gl::Context *value)
38 {
39 #if defined(ANGLE_PLATFORM_ANDROID)
40 if (angle::gUseAndroidOpenGLTlsSlot)
41 {
42 ANGLE_ANDROID_GET_GL_TLS()[angle::kAndroidOpenGLTlsSlot] = static_cast<void *>(value);
43 }
44 #endif
45 }
46
AllocateCurrentThread()47 Thread *AllocateCurrentThread()
48 {
49 Thread *thread;
50 {
51 // Global thread intentionally leaked
52 ANGLE_SCOPED_DISABLE_LSAN();
53 thread = new Thread();
54 #if defined(ANGLE_PLATFORM_APPLE)
55 SetCurrentThreadTLS(thread);
56 #else
57 gCurrentThread = thread;
58 #endif
59 }
60
61 // Initialize fast TLS slot
62 SetContextToAndroidOpenGLTLSSlot(nullptr);
63
64 #if defined(ANGLE_PLATFORM_APPLE)
65 gl::SetCurrentValidContextTLS(nullptr);
66 #else
67 gl::gCurrentValidContext = nullptr;
68 #endif
69
70 #if defined(ANGLE_PLATFORM_ANDROID)
71 static pthread_once_t keyOnce = PTHREAD_ONCE_INIT;
72 static TLSIndex gProcessCleanupTLSIndex = TLS_INVALID_INDEX;
73
74 // Create process cleanup TLS slot
75 auto CreateProcessCleanupTLSIndex = []() {
76 gProcessCleanupTLSIndex = CreateTLSIndex(angle::ProcessCleanupCallback);
77 };
78 pthread_once(&keyOnce, CreateProcessCleanupTLSIndex);
79 ASSERT(gProcessCleanupTLSIndex != TLS_INVALID_INDEX);
80
81 // Initialize process cleanup TLS slot
82 angle::gProcessCleanupRefCount++;
83 SetTLSValue(gProcessCleanupTLSIndex, thread);
84 #endif // ANGLE_PLATFORM_ANDROID
85
86 ASSERT(thread);
87 return thread;
88 }
89
AllocateGlobalMutex(std::atomic<angle::GlobalMutex * > & mutex)90 void AllocateGlobalMutex(std::atomic<angle::GlobalMutex *> &mutex)
91 {
92 if (mutex == nullptr)
93 {
94 std::unique_ptr<angle::GlobalMutex> newMutex(new angle::GlobalMutex());
95 angle::GlobalMutex *expected = nullptr;
96 if (mutex.compare_exchange_strong(expected, newMutex.get()))
97 {
98 newMutex.release();
99 }
100 }
101 }
102
AllocateMutex()103 void AllocateMutex()
104 {
105 AllocateGlobalMutex(g_Mutex);
106 }
107
AllocateSurfaceMutex()108 void AllocateSurfaceMutex()
109 {
110 AllocateGlobalMutex(g_SurfaceMutex);
111 }
112
113 } // anonymous namespace
114
115 #if defined(ANGLE_PLATFORM_APPLE)
116 // TODO(angleproject:6479): Due to a bug in Apple's dyld loader, `thread_local` will cause
117 // excessive memory use. Temporarily avoid it by using pthread's thread
118 // local storage instead.
119 // https://bugs.webkit.org/show_bug.cgi?id=228240
120
GetCurrentThreadTLSIndex()121 static TLSIndex GetCurrentThreadTLSIndex()
122 {
123 static TLSIndex CurrentThreadIndex = TLS_INVALID_INDEX;
124 static dispatch_once_t once;
125 dispatch_once(&once, ^{
126 ASSERT(CurrentThreadIndex == TLS_INVALID_INDEX);
127 CurrentThreadIndex = CreateTLSIndex(nullptr);
128 });
129 return CurrentThreadIndex;
130 }
GetCurrentThreadTLS()131 Thread *GetCurrentThreadTLS()
132 {
133 TLSIndex CurrentThreadIndex = GetCurrentThreadTLSIndex();
134 ASSERT(CurrentThreadIndex != TLS_INVALID_INDEX);
135 return static_cast<Thread *>(GetTLSValue(CurrentThreadIndex));
136 }
SetCurrentThreadTLS(Thread * thread)137 void SetCurrentThreadTLS(Thread *thread)
138 {
139 TLSIndex CurrentThreadIndex = GetCurrentThreadTLSIndex();
140 ASSERT(CurrentThreadIndex != TLS_INVALID_INDEX);
141 SetTLSValue(CurrentThreadIndex, thread);
142 }
143 #else
144 thread_local Thread *gCurrentThread = nullptr;
145 #endif
146
GetGlobalMutex()147 angle::GlobalMutex &GetGlobalMutex()
148 {
149 AllocateMutex();
150 return *g_Mutex;
151 }
152
GetGlobalSurfaceMutex()153 angle::GlobalMutex &GetGlobalSurfaceMutex()
154 {
155 AllocateSurfaceMutex();
156 return *g_SurfaceMutex;
157 }
158
GetGlobalLastContext()159 gl::Context *GetGlobalLastContext()
160 {
161 return g_LastContext;
162 }
163
SetGlobalLastContext(gl::Context * context)164 void SetGlobalLastContext(gl::Context *context)
165 {
166 g_LastContext = context;
167 }
168
169 // This function causes an MSAN false positive, which is muted. See https://crbug.com/1211047
170 // It also causes a flaky false positive in TSAN. http://crbug.com/1223970
GetCurrentThread()171 ANGLE_NO_SANITIZE_MEMORY ANGLE_NO_SANITIZE_THREAD Thread *GetCurrentThread()
172 {
173 #if defined(ANGLE_PLATFORM_APPLE)
174 Thread *current = GetCurrentThreadTLS();
175 #else
176 Thread *current = gCurrentThread;
177 #endif
178 return (current ? current : AllocateCurrentThread());
179 }
180
SetContextCurrent(Thread * thread,gl::Context * context)181 void SetContextCurrent(Thread *thread, gl::Context *context)
182 {
183 #if defined(ANGLE_PLATFORM_APPLE)
184 Thread *currentThread = GetCurrentThreadTLS();
185 #else
186 Thread *currentThread = gCurrentThread;
187 #endif
188 ASSERT(currentThread);
189 currentThread->setCurrent(context);
190 SetContextToAndroidOpenGLTLSSlot(context);
191
192 #if defined(ANGLE_PLATFORM_APPLE)
193 gl::SetCurrentValidContextTLS(context);
194 #else
195 gl::gCurrentValidContext = context;
196 #endif
197
198 #if defined(ANGLE_FORCE_CONTEXT_CHECK_EVERY_CALL)
199 DirtyContextIfNeeded(context);
200 #endif
201 }
202
ScopedSyncCurrentContextFromThread(egl::Thread * thread)203 ScopedSyncCurrentContextFromThread::ScopedSyncCurrentContextFromThread(egl::Thread *thread)
204 : mThread(thread)
205 {
206 ASSERT(mThread);
207 }
208
~ScopedSyncCurrentContextFromThread()209 ScopedSyncCurrentContextFromThread::~ScopedSyncCurrentContextFromThread()
210 {
211 SetContextCurrent(mThread, mThread->getContext());
212 }
213
214 } // namespace egl
215
216 namespace gl
217 {
GenerateContextLostErrorOnContext(Context * context)218 void GenerateContextLostErrorOnContext(Context *context)
219 {
220 if (context && context->isContextLost())
221 {
222 context->validationError(angle::EntryPoint::GLInvalid, GL_CONTEXT_LOST, err::kContextLost);
223 }
224 }
225
GenerateContextLostErrorOnCurrentGlobalContext()226 void GenerateContextLostErrorOnCurrentGlobalContext()
227 {
228 GenerateContextLostErrorOnContext(GetGlobalContext());
229 }
230 } // namespace gl
231
232 #if defined(ANGLE_PLATFORM_WINDOWS) && !defined(ANGLE_STATIC)
233 namespace egl
234 {
235
236 namespace
237 {
238
DeallocateGlobalMutex(std::atomic<angle::GlobalMutex * > & mutex)239 void DeallocateGlobalMutex(std::atomic<angle::GlobalMutex *> &mutex)
240 {
241 angle::GlobalMutex *toDelete = mutex.exchange(nullptr);
242 if (!mutex)
243 return;
244 {
245 // Wait for toDelete to become released by other threads before deleting.
246 std::lock_guard<angle::GlobalMutex> lock(*toDelete);
247 }
248 SafeDelete(toDelete);
249 }
250
DeallocateCurrentThread()251 void DeallocateCurrentThread()
252 {
253 SafeDelete(gCurrentThread);
254 }
255
DeallocateMutex()256 void DeallocateMutex()
257 {
258 DeallocateGlobalMutex(g_Mutex);
259 }
260
DeallocateSurfaceMutex()261 void DeallocateSurfaceMutex()
262 {
263 DeallocateGlobalMutex(g_SurfaceMutex);
264 }
265
InitializeProcess()266 bool InitializeProcess()
267 {
268 EnsureDebugAllocated();
269 AllocateMutex();
270 return AllocateCurrentThread() != nullptr;
271 }
272
TerminateProcess()273 void TerminateProcess()
274 {
275 DeallocateDebug();
276 DeallocateSurfaceMutex();
277 DeallocateMutex();
278 DeallocateCurrentThread();
279 }
280
281 } // anonymous namespace
282
283 } // namespace egl
284
285 namespace
286 {
287 // The following WaitForDebugger code is based on SwiftShader. See:
288 // https://cs.chromium.org/chromium/src/third_party/swiftshader/src/Vulkan/main.cpp
289 # if defined(ANGLE_ENABLE_ASSERTS) && !defined(ANGLE_ENABLE_WINDOWS_UWP)
DebuggerWaitDialogProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)290 INT_PTR CALLBACK DebuggerWaitDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
291 {
292 RECT rect;
293
294 switch (uMsg)
295 {
296 case WM_INITDIALOG:
297 ::GetWindowRect(GetDesktopWindow(), &rect);
298 ::SetWindowPos(hwnd, HWND_TOP, rect.right / 2, rect.bottom / 2, 0, 0, SWP_NOSIZE);
299 ::SetTimer(hwnd, 1, 100, NULL);
300 return TRUE;
301 case WM_COMMAND:
302 if (LOWORD(wParam) == IDCANCEL)
303 {
304 ::EndDialog(hwnd, 0);
305 }
306 break;
307 case WM_TIMER:
308 if (angle::IsDebuggerAttached())
309 {
310 ::EndDialog(hwnd, 0);
311 }
312 }
313
314 return FALSE;
315 }
316
WaitForDebugger(HINSTANCE instance)317 void WaitForDebugger(HINSTANCE instance)
318 {
319 if (angle::IsDebuggerAttached())
320 return;
321
322 HRSRC dialog = ::FindResourceA(instance, MAKEINTRESOURCEA(IDD_DIALOG1), MAKEINTRESOURCEA(5));
323 if (!dialog)
324 {
325 printf("Error finding wait for debugger dialog. Error %lu.\n", ::GetLastError());
326 return;
327 }
328
329 DLGTEMPLATE *dialogTemplate = reinterpret_cast<DLGTEMPLATE *>(::LoadResource(instance, dialog));
330 ::DialogBoxIndirectA(instance, dialogTemplate, NULL, DebuggerWaitDialogProc);
331 }
332 # else
333 void WaitForDebugger(HINSTANCE instance) {}
334 # endif // defined(ANGLE_ENABLE_ASSERTS) && !defined(ANGLE_ENABLE_WINDOWS_UWP)
335 } // namespace
336
DllMain(HINSTANCE instance,DWORD reason,LPVOID)337 extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID)
338 {
339 switch (reason)
340 {
341 case DLL_PROCESS_ATTACH:
342 if (angle::GetEnvironmentVar("ANGLE_WAIT_FOR_DEBUGGER") == "1")
343 {
344 WaitForDebugger(instance);
345 }
346 return static_cast<BOOL>(egl::InitializeProcess());
347
348 case DLL_THREAD_ATTACH:
349 return static_cast<BOOL>(egl::AllocateCurrentThread() != nullptr);
350
351 case DLL_THREAD_DETACH:
352 egl::DeallocateCurrentThread();
353 break;
354
355 case DLL_PROCESS_DETACH:
356 egl::TerminateProcess();
357 break;
358 }
359
360 return TRUE;
361 }
362 #endif // defined(ANGLE_PLATFORM_WINDOWS) && !defined(ANGLE_STATIC)
363