1 /**************************************************************************
2 *
3 * Copyright 2009-2010 Chia-I Wu <olvaffe@gmail.com>
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include "c99_compat.h"
34 #include "c11/threads.h"
35 #include "util/u_thread.h"
36 #include "util/u_string.h"
37
38 #include "egllog.h"
39 #include "eglcurrent.h"
40 #include "eglglobals.h"
41
42 /* a fallback thread info to guarantee that every thread always has one */
43 static _EGLThreadInfo dummy_thread;
44 static mtx_t _egl_TSDMutex = _MTX_INITIALIZER_NP;
45 static EGLBoolean _egl_TSDInitialized;
46 static tss_t _egl_TSD;
47 static void _eglDestroyThreadInfo(_EGLThreadInfo *t);
48
49 #ifdef USE_ELF_TLS
50 static __THREAD_INITIAL_EXEC const _EGLThreadInfo *_egl_TLS;
51 #endif
52
_eglSetTSD(const _EGLThreadInfo * t)53 static inline void _eglSetTSD(const _EGLThreadInfo *t)
54 {
55 tss_set(_egl_TSD, (void *) t);
56 #ifdef USE_ELF_TLS
57 _egl_TLS = t;
58 #endif
59 }
60
_eglGetTSD(void)61 static inline _EGLThreadInfo *_eglGetTSD(void)
62 {
63 #ifdef USE_ELF_TLS
64 return (_EGLThreadInfo *) _egl_TLS;
65 #else
66 return (_EGLThreadInfo *) tss_get(_egl_TSD);
67 #endif
68 }
69
_eglFiniTSD(void)70 static inline void _eglFiniTSD(void)
71 {
72 mtx_lock(&_egl_TSDMutex);
73 if (_egl_TSDInitialized) {
74 _EGLThreadInfo *t = _eglGetTSD();
75
76 _egl_TSDInitialized = EGL_FALSE;
77 _eglDestroyThreadInfo(t);
78 tss_delete(_egl_TSD);
79 }
80 mtx_unlock(&_egl_TSDMutex);
81 }
82
_eglInitTSD()83 static inline EGLBoolean _eglInitTSD()
84 {
85 if (!_egl_TSDInitialized) {
86 mtx_lock(&_egl_TSDMutex);
87
88 /* check again after acquiring lock */
89 if (!_egl_TSDInitialized) {
90 if (tss_create(&_egl_TSD, (void (*)(void *)) _eglDestroyThreadInfo) != thrd_success) {
91 mtx_unlock(&_egl_TSDMutex);
92 return EGL_FALSE;
93 }
94 _eglAddAtExitCall(_eglFiniTSD);
95 _egl_TSDInitialized = EGL_TRUE;
96 }
97
98 mtx_unlock(&_egl_TSDMutex);
99 }
100
101 return EGL_TRUE;
102 }
103
104 static void
_eglInitThreadInfo(_EGLThreadInfo * t)105 _eglInitThreadInfo(_EGLThreadInfo *t)
106 {
107 t->LastError = EGL_SUCCESS;
108 /* default, per EGL spec */
109 t->CurrentAPI = EGL_OPENGL_ES_API;
110 }
111
112
113 /**
114 * Allocate and init a new _EGLThreadInfo object.
115 */
116 static _EGLThreadInfo *
_eglCreateThreadInfo(void)117 _eglCreateThreadInfo(void)
118 {
119 _EGLThreadInfo *t = calloc(1, sizeof(_EGLThreadInfo));
120 if (!t)
121 t = &dummy_thread;
122
123 _eglInitThreadInfo(t);
124 return t;
125 }
126
127
128 /**
129 * Delete/free a _EGLThreadInfo object.
130 */
131 static void
_eglDestroyThreadInfo(_EGLThreadInfo * t)132 _eglDestroyThreadInfo(_EGLThreadInfo *t)
133 {
134 if (t != &dummy_thread) {
135 free(t);
136 #ifdef USE_ELF_TLS
137 /* Reset the TLS also here, otherwise
138 * it will be having a dangling pointer */
139 _egl_TLS = NULL;
140 #endif
141 }
142 }
143
144
145 /**
146 * Make sure TSD is initialized and return current value.
147 */
148 static inline _EGLThreadInfo *
_eglCheckedGetTSD(void)149 _eglCheckedGetTSD(void)
150 {
151 if (_eglInitTSD() != EGL_TRUE) {
152 _eglLog(_EGL_FATAL, "failed to initialize \"current\" system");
153 return NULL;
154 }
155
156 return _eglGetTSD();
157 }
158
159
160 /**
161 * Return the calling thread's thread info.
162 * If the calling thread nevers calls this function before, or if its thread
163 * info was destroyed, a new one is created. This function never returns NULL.
164 * In the case allocation fails, a dummy one is returned. See also
165 * _eglIsCurrentThreadDummy.
166 */
167 _EGLThreadInfo *
_eglGetCurrentThread(void)168 _eglGetCurrentThread(void)
169 {
170 _EGLThreadInfo *t = _eglCheckedGetTSD();
171 if (!t) {
172 t = _eglCreateThreadInfo();
173 _eglSetTSD(t);
174 }
175
176 return t;
177 }
178
179
180 /**
181 * Destroy the calling thread's thread info.
182 */
183 void
_eglDestroyCurrentThread(void)184 _eglDestroyCurrentThread(void)
185 {
186 _EGLThreadInfo *t = _eglCheckedGetTSD();
187 if (t) {
188 _eglDestroyThreadInfo(t);
189 _eglSetTSD(NULL);
190 }
191 }
192
193
194 /**
195 * Return true if the calling thread's thread info is dummy.
196 * A dummy thread info is shared by all threads and should not be modified.
197 * Functions like eglBindAPI or eglMakeCurrent should check for dummy-ness
198 * before updating the thread info.
199 */
200 EGLBoolean
_eglIsCurrentThreadDummy(void)201 _eglIsCurrentThreadDummy(void)
202 {
203 _EGLThreadInfo *t = _eglCheckedGetTSD();
204 return (!t || t == &dummy_thread);
205 }
206
207
208 /**
209 * Return the currently bound context of the current API, or NULL.
210 */
211 _EGLContext *
_eglGetCurrentContext(void)212 _eglGetCurrentContext(void)
213 {
214 _EGLThreadInfo *t = _eglGetCurrentThread();
215 return t->CurrentContext;
216 }
217
218
219 /**
220 * Record EGL error code and return EGL_FALSE.
221 */
222 static EGLBoolean
_eglInternalError(EGLint errCode,const char * msg)223 _eglInternalError(EGLint errCode, const char *msg)
224 {
225 _EGLThreadInfo *t = _eglGetCurrentThread();
226
227 if (t == &dummy_thread)
228 return EGL_FALSE;
229
230 t->LastError = errCode;
231
232 if (errCode != EGL_SUCCESS) {
233 const char *s;
234
235 switch (errCode) {
236 case EGL_BAD_ACCESS:
237 s = "EGL_BAD_ACCESS";
238 break;
239 case EGL_BAD_ALLOC:
240 s = "EGL_BAD_ALLOC";
241 break;
242 case EGL_BAD_ATTRIBUTE:
243 s = "EGL_BAD_ATTRIBUTE";
244 break;
245 case EGL_BAD_CONFIG:
246 s = "EGL_BAD_CONFIG";
247 break;
248 case EGL_BAD_CONTEXT:
249 s = "EGL_BAD_CONTEXT";
250 break;
251 case EGL_BAD_CURRENT_SURFACE:
252 s = "EGL_BAD_CURRENT_SURFACE";
253 break;
254 case EGL_BAD_DISPLAY:
255 s = "EGL_BAD_DISPLAY";
256 break;
257 case EGL_BAD_MATCH:
258 s = "EGL_BAD_MATCH";
259 break;
260 case EGL_BAD_NATIVE_PIXMAP:
261 s = "EGL_BAD_NATIVE_PIXMAP";
262 break;
263 case EGL_BAD_NATIVE_WINDOW:
264 s = "EGL_BAD_NATIVE_WINDOW";
265 break;
266 case EGL_BAD_PARAMETER:
267 s = "EGL_BAD_PARAMETER";
268 break;
269 case EGL_BAD_SURFACE:
270 s = "EGL_BAD_SURFACE";
271 break;
272 case EGL_NOT_INITIALIZED:
273 s = "EGL_NOT_INITIALIZED";
274 break;
275 default:
276 s = "other EGL error";
277 }
278 _eglLog(_EGL_DEBUG, "EGL user error 0x%x (%s) in %s\n", errCode, s, msg);
279 }
280
281 return EGL_FALSE;
282 }
283
284 EGLBoolean
_eglError(EGLint errCode,const char * msg)285 _eglError(EGLint errCode, const char *msg)
286 {
287 if (errCode != EGL_SUCCESS) {
288 EGLint type;
289 if (errCode == EGL_BAD_ALLOC)
290 type = EGL_DEBUG_MSG_CRITICAL_KHR;
291 else
292 type = EGL_DEBUG_MSG_ERROR_KHR;
293
294 _eglDebugReport(errCode, NULL, type, msg);
295 } else
296 _eglInternalError(errCode, msg);
297
298 return EGL_FALSE;
299 }
300
301 void
_eglDebugReport(EGLenum error,const char * funcName,EGLint type,const char * message,...)302 _eglDebugReport(EGLenum error, const char *funcName,
303 EGLint type, const char *message, ...)
304 {
305 _EGLThreadInfo *thr = _eglGetCurrentThread();
306 EGLDEBUGPROCKHR callback = NULL;
307 va_list args;
308
309 if (funcName == NULL)
310 funcName = thr->CurrentFuncName;
311
312 mtx_lock(_eglGlobal.Mutex);
313 if (_eglGlobal.debugTypesEnabled & DebugBitFromType(type))
314 callback = _eglGlobal.debugCallback;
315
316 mtx_unlock(_eglGlobal.Mutex);
317
318 char *message_buf = NULL;
319 if (message != NULL) {
320 va_start(args, message);
321 if (vasprintf(&message_buf, message, args) < 0)
322 message_buf = NULL;
323 va_end(args);
324 }
325
326 if (callback != NULL) {
327 callback(error, funcName, type, thr->Label, thr->CurrentObjectLabel,
328 message_buf);
329 }
330
331 if (type == EGL_DEBUG_MSG_CRITICAL_KHR || type == EGL_DEBUG_MSG_ERROR_KHR) {
332 char *func_message_buf = NULL;
333 /* Note: _eglError() is often called with msg == thr->currentFuncName */
334 if (message_buf && funcName && strcmp(message_buf, funcName) != 0) {
335 if (asprintf(&func_message_buf, "%s: %s", funcName, message_buf) < 0)
336 func_message_buf = NULL;
337 }
338 _eglInternalError(error, func_message_buf ? func_message_buf : funcName);
339 free(func_message_buf);
340 }
341 free(message_buf);
342 }
343