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 "common/tls.h"
15 #include "libGLESv2/resource.h"
16
17 #include <atomic>
18
19 namespace gl
20 {
21 // In single-threaded cases we can avoid a TLS lookup for the current Context.
22 //
23 // Let a global single-threaded context have 3 states: unset, set, and multi-threaded.
24 // Initially it is unset. Then, on MakeCurrent:
25 //
26 // * if the ST context is unset -> set the global context.
27 // * if the ST context is set and matches the TLS -> set the global context.
28 // * if the ST context is set and does not match TLS -> set multi-threaded mode.
29 // * if in multi-threaded mode, unset and subsequently ignore the global context.
30 //
31 // Implementation-wise we can use a pointer and a boolean to represent the three modes.
32 Context *gSingleThreadedContext = nullptr;
33 bool gIsMultiThreadedContext = false;
34 } // namespace gl
35
36 namespace egl
37 {
38 namespace
39 {
40 static TLSIndex threadTLS = TLS_INVALID_INDEX;
41 Debug *g_Debug = nullptr;
42
43 ANGLE_REQUIRE_CONSTANT_INIT std::atomic<angle::GlobalMutex *> g_Mutex(nullptr);
44 static_assert(std::is_trivially_destructible<decltype(g_Mutex)>::value,
45 "global mutex is not trivially destructible");
46
AllocateCurrentThread()47 Thread *AllocateCurrentThread()
48 {
49 ASSERT(threadTLS != TLS_INVALID_INDEX);
50 if (threadTLS == TLS_INVALID_INDEX)
51 {
52 return nullptr;
53 }
54
55 Thread *thread = new Thread();
56 if (!SetTLSValue(threadTLS, thread))
57 {
58 ERR() << "Could not set thread local storage.";
59 return nullptr;
60 }
61
62 return thread;
63 }
64
AllocateDebug()65 void AllocateDebug()
66 {
67 // All EGL calls use a global lock, this is thread safe
68 if (g_Debug == nullptr)
69 {
70 g_Debug = new Debug();
71 }
72 }
73
AllocateMutex()74 void AllocateMutex()
75 {
76 if (g_Mutex == nullptr)
77 {
78 std::unique_ptr<angle::GlobalMutex> newMutex(new angle::GlobalMutex());
79 angle::GlobalMutex *expected = nullptr;
80 if (g_Mutex.compare_exchange_strong(expected, newMutex.get()))
81 {
82 newMutex.release();
83 }
84 }
85 }
86
87 } // anonymous namespace
88
GetGlobalMutex()89 angle::GlobalMutex &GetGlobalMutex()
90 {
91 AllocateMutex();
92 return *g_Mutex;
93 }
94
GetCurrentThread()95 Thread *GetCurrentThread()
96 {
97 // Create a TLS index if one has not been created for this DLL
98 if (threadTLS == TLS_INVALID_INDEX)
99 {
100 threadTLS = CreateTLSIndex();
101 }
102
103 Thread *current = static_cast<Thread *>(GetTLSValue(threadTLS));
104
105 // ANGLE issue 488: when the dll is loaded after thread initialization,
106 // thread local storage (current) might not exist yet.
107 return (current ? current : AllocateCurrentThread());
108 }
109
GetDebug()110 Debug *GetDebug()
111 {
112 AllocateDebug();
113 return g_Debug;
114 }
115
SetContextCurrent(Thread * thread,gl::Context * context)116 void SetContextCurrent(Thread *thread, gl::Context *context)
117 {
118 // See above comment on gGlobalContext.
119 // If the context is in multi-threaded mode, ignore the global context.
120 if (!gl::gIsMultiThreadedContext)
121 {
122 // If the global context is unset or matches the current TLS, set the global context.
123 if (gl::gSingleThreadedContext == nullptr ||
124 gl::gSingleThreadedContext == thread->getContext())
125 {
126 gl::gSingleThreadedContext = context;
127 }
128 else
129 {
130 // If the global context is set and does not match TLS, set multi-threaded mode.
131 gl::gSingleThreadedContext = nullptr;
132 gl::gIsMultiThreadedContext = true;
133 }
134 }
135 thread->setCurrent(context);
136 }
137 } // namespace egl
138
139 #ifdef ANGLE_PLATFORM_WINDOWS
140 namespace egl
141 {
142
143 namespace
144 {
145
DeallocateCurrentThread()146 bool DeallocateCurrentThread()
147 {
148 Thread *thread = static_cast<Thread *>(GetTLSValue(threadTLS));
149 SafeDelete(thread);
150 return SetTLSValue(threadTLS, nullptr);
151 }
152
DeallocateDebug()153 void DeallocateDebug()
154 {
155 SafeDelete(g_Debug);
156 }
157
DeallocateMutex()158 void DeallocateMutex()
159 {
160 angle::GlobalMutex *mutex = g_Mutex.exchange(nullptr);
161 {
162 // Wait for the mutex to become released by other threads before deleting.
163 std::lock_guard<angle::GlobalMutex> lock(*mutex);
164 }
165 SafeDelete(mutex);
166 }
167
InitializeProcess()168 bool InitializeProcess()
169 {
170 ASSERT(g_Debug == nullptr);
171 AllocateDebug();
172
173 AllocateMutex();
174
175 threadTLS = CreateTLSIndex();
176 if (threadTLS == TLS_INVALID_INDEX)
177 {
178 return false;
179 }
180
181 return AllocateCurrentThread() != nullptr;
182 }
183
TerminateProcess()184 bool TerminateProcess()
185 {
186 DeallocateDebug();
187
188 DeallocateMutex();
189
190 if (!DeallocateCurrentThread())
191 {
192 return false;
193 }
194
195 if (threadTLS != TLS_INVALID_INDEX)
196 {
197 TLSIndex tlsCopy = threadTLS;
198 threadTLS = TLS_INVALID_INDEX;
199
200 if (!DestroyTLSIndex(tlsCopy))
201 {
202 return false;
203 }
204 }
205
206 return true;
207 }
208
209 } // anonymous namespace
210
211 } // namespace egl
212
213 namespace
214 {
215 // The following WaitForDebugger code is based on SwiftShader. See:
216 // https://cs.chromium.org/chromium/src/third_party/swiftshader/src/Vulkan/main.cpp
217 # if defined(ANGLE_ENABLE_ASSERTS) && !defined(ANGLE_ENABLE_WINDOWS_UWP)
DebuggerWaitDialogProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)218 INT_PTR CALLBACK DebuggerWaitDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
219 {
220 RECT rect;
221
222 switch (uMsg)
223 {
224 case WM_INITDIALOG:
225 ::GetWindowRect(GetDesktopWindow(), &rect);
226 ::SetWindowPos(hwnd, HWND_TOP, rect.right / 2, rect.bottom / 2, 0, 0, SWP_NOSIZE);
227 ::SetTimer(hwnd, 1, 100, NULL);
228 return TRUE;
229 case WM_COMMAND:
230 if (LOWORD(wParam) == IDCANCEL)
231 {
232 ::EndDialog(hwnd, 0);
233 }
234 break;
235 case WM_TIMER:
236 if (angle::IsDebuggerAttached())
237 {
238 ::EndDialog(hwnd, 0);
239 }
240 }
241
242 return FALSE;
243 }
244
WaitForDebugger(HINSTANCE instance)245 void WaitForDebugger(HINSTANCE instance)
246 {
247 if (angle::IsDebuggerAttached())
248 return;
249
250 HRSRC dialog = ::FindResourceA(instance, MAKEINTRESOURCEA(IDD_DIALOG1), MAKEINTRESOURCEA(5));
251 if (!dialog)
252 {
253 printf("Error finding wait for debugger dialog. Error %lu.\n", ::GetLastError());
254 return;
255 }
256
257 DLGTEMPLATE *dialogTemplate = reinterpret_cast<DLGTEMPLATE *>(::LoadResource(instance, dialog));
258 ::DialogBoxIndirectA(instance, dialogTemplate, NULL, DebuggerWaitDialogProc);
259 }
260 # else
261 void WaitForDebugger(HINSTANCE instance) {}
262 # endif // defined(ANGLE_ENABLE_ASSERTS) && !defined(ANGLE_ENABLE_WINDOWS_UWP)
263 } // namespace
264
DllMain(HINSTANCE instance,DWORD reason,LPVOID)265 extern "C" BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID)
266 {
267 switch (reason)
268 {
269 case DLL_PROCESS_ATTACH:
270 if (angle::GetEnvironmentVar("ANGLE_WAIT_FOR_DEBUGGER") == "1")
271 {
272 WaitForDebugger(instance);
273 }
274 return static_cast<BOOL>(egl::InitializeProcess());
275
276 case DLL_THREAD_ATTACH:
277 return static_cast<BOOL>(egl::AllocateCurrentThread() != nullptr);
278
279 case DLL_THREAD_DETACH:
280 return static_cast<BOOL>(egl::DeallocateCurrentThread());
281
282 case DLL_PROCESS_DETACH:
283 return static_cast<BOOL>(egl::TerminateProcess());
284 }
285
286 return TRUE;
287 }
288 #endif // ANGLE_PLATFORM_WINDOWS
289