• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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