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
36 #include "egllog.h"
37 #include "eglcurrent.h"
38 #include "eglglobals.h"
39
40 /* a fallback thread info to guarantee that every thread always has one */
41 static _EGLThreadInfo dummy_thread;
42 static mtx_t _egl_TSDMutex = _MTX_INITIALIZER_NP;
43 static EGLBoolean _egl_TSDInitialized;
44 static tss_t _egl_TSD;
45 static void _eglDestroyThreadInfo(_EGLThreadInfo *t);
46
47 #ifdef USE_ELF_TLS
48 static __thread const _EGLThreadInfo *_egl_TLS
49 __attribute__ ((tls_model("initial-exec")));
50 #endif
51
_eglSetTSD(const _EGLThreadInfo * t)52 static inline void _eglSetTSD(const _EGLThreadInfo *t)
53 {
54 tss_set(_egl_TSD, (void *) t);
55 #ifdef USE_ELF_TLS
56 _egl_TLS = t;
57 #endif
58 }
59
_eglGetTSD(void)60 static inline _EGLThreadInfo *_eglGetTSD(void)
61 {
62 #ifdef USE_ELF_TLS
63 return (_EGLThreadInfo *) _egl_TLS;
64 #else
65 return (_EGLThreadInfo *) tss_get(_egl_TSD);
66 #endif
67 }
68
_eglFiniTSD(void)69 static inline void _eglFiniTSD(void)
70 {
71 mtx_lock(&_egl_TSDMutex);
72 if (_egl_TSDInitialized) {
73 _EGLThreadInfo *t = _eglGetTSD();
74
75 _egl_TSDInitialized = EGL_FALSE;
76 _eglDestroyThreadInfo(t);
77 tss_delete(_egl_TSD);
78 }
79 mtx_unlock(&_egl_TSDMutex);
80 }
81
_eglInitTSD()82 static inline EGLBoolean _eglInitTSD()
83 {
84 if (!_egl_TSDInitialized) {
85 mtx_lock(&_egl_TSDMutex);
86
87 /* check again after acquiring lock */
88 if (!_egl_TSDInitialized) {
89 if (tss_create(&_egl_TSD, (void (*)(void *)) _eglDestroyThreadInfo) != thrd_success) {
90 mtx_unlock(&_egl_TSDMutex);
91 return EGL_FALSE;
92 }
93 _eglAddAtExitCall(_eglFiniTSD);
94 _egl_TSDInitialized = EGL_TRUE;
95 }
96
97 mtx_unlock(&_egl_TSDMutex);
98 }
99
100 return EGL_TRUE;
101 }
102
103 static void
_eglInitThreadInfo(_EGLThreadInfo * t)104 _eglInitThreadInfo(_EGLThreadInfo *t)
105 {
106 t->LastError = EGL_SUCCESS;
107 /* default, per EGL spec */
108 t->CurrentAPI = EGL_OPENGL_ES_API;
109 }
110
111
112 /**
113 * Allocate and init a new _EGLThreadInfo object.
114 */
115 static _EGLThreadInfo *
_eglCreateThreadInfo(void)116 _eglCreateThreadInfo(void)
117 {
118 _EGLThreadInfo *t = calloc(1, sizeof(_EGLThreadInfo));
119 if (!t)
120 t = &dummy_thread;
121
122 _eglInitThreadInfo(t);
123 return t;
124 }
125
126
127 /**
128 * Delete/free a _EGLThreadInfo object.
129 */
130 static void
_eglDestroyThreadInfo(_EGLThreadInfo * t)131 _eglDestroyThreadInfo(_EGLThreadInfo *t)
132 {
133 if (t != &dummy_thread)
134 free(t);
135 }
136
137
138 /**
139 * Make sure TSD is initialized and return current value.
140 */
141 static inline _EGLThreadInfo *
_eglCheckedGetTSD(void)142 _eglCheckedGetTSD(void)
143 {
144 if (_eglInitTSD() != EGL_TRUE) {
145 _eglLog(_EGL_FATAL, "failed to initialize \"current\" system");
146 return NULL;
147 }
148
149 return _eglGetTSD();
150 }
151
152
153 /**
154 * Return the calling thread's thread info.
155 * If the calling thread nevers calls this function before, or if its thread
156 * info was destroyed, a new one is created. This function never returns NULL.
157 * In the case allocation fails, a dummy one is returned. See also
158 * _eglIsCurrentThreadDummy.
159 */
160 _EGLThreadInfo *
_eglGetCurrentThread(void)161 _eglGetCurrentThread(void)
162 {
163 _EGLThreadInfo *t = _eglCheckedGetTSD();
164 if (!t) {
165 t = _eglCreateThreadInfo();
166 _eglSetTSD(t);
167 }
168
169 return t;
170 }
171
172
173 /**
174 * Destroy the calling thread's thread info.
175 */
176 void
_eglDestroyCurrentThread(void)177 _eglDestroyCurrentThread(void)
178 {
179 _EGLThreadInfo *t = _eglCheckedGetTSD();
180 if (t) {
181 _eglDestroyThreadInfo(t);
182 _eglSetTSD(NULL);
183 }
184 }
185
186
187 /**
188 * Return true if the calling thread's thread info is dummy.
189 * A dummy thread info is shared by all threads and should not be modified.
190 * Functions like eglBindAPI or eglMakeCurrent should check for dummy-ness
191 * before updating the thread info.
192 */
193 EGLBoolean
_eglIsCurrentThreadDummy(void)194 _eglIsCurrentThreadDummy(void)
195 {
196 _EGLThreadInfo *t = _eglCheckedGetTSD();
197 return (!t || t == &dummy_thread);
198 }
199
200
201 /**
202 * Return the currently bound context of the current API, or NULL.
203 */
204 _EGLContext *
_eglGetCurrentContext(void)205 _eglGetCurrentContext(void)
206 {
207 _EGLThreadInfo *t = _eglGetCurrentThread();
208 return t->CurrentContext;
209 }
210
211
212 /**
213 * Record EGL error code and return EGL_FALSE.
214 */
215 static EGLBoolean
_eglInternalError(EGLint errCode,const char * msg)216 _eglInternalError(EGLint errCode, const char *msg)
217 {
218 _EGLThreadInfo *t = _eglGetCurrentThread();
219
220 if (t == &dummy_thread)
221 return EGL_FALSE;
222
223 t->LastError = errCode;
224
225 if (errCode != EGL_SUCCESS) {
226 const char *s;
227
228 switch (errCode) {
229 case EGL_BAD_ACCESS:
230 s = "EGL_BAD_ACCESS";
231 break;
232 case EGL_BAD_ALLOC:
233 s = "EGL_BAD_ALLOC";
234 break;
235 case EGL_BAD_ATTRIBUTE:
236 s = "EGL_BAD_ATTRIBUTE";
237 break;
238 case EGL_BAD_CONFIG:
239 s = "EGL_BAD_CONFIG";
240 break;
241 case EGL_BAD_CONTEXT:
242 s = "EGL_BAD_CONTEXT";
243 break;
244 case EGL_BAD_CURRENT_SURFACE:
245 s = "EGL_BAD_CURRENT_SURFACE";
246 break;
247 case EGL_BAD_DISPLAY:
248 s = "EGL_BAD_DISPLAY";
249 break;
250 case EGL_BAD_MATCH:
251 s = "EGL_BAD_MATCH";
252 break;
253 case EGL_BAD_NATIVE_PIXMAP:
254 s = "EGL_BAD_NATIVE_PIXMAP";
255 break;
256 case EGL_BAD_NATIVE_WINDOW:
257 s = "EGL_BAD_NATIVE_WINDOW";
258 break;
259 case EGL_BAD_PARAMETER:
260 s = "EGL_BAD_PARAMETER";
261 break;
262 case EGL_BAD_SURFACE:
263 s = "EGL_BAD_SURFACE";
264 break;
265 case EGL_NOT_INITIALIZED:
266 s = "EGL_NOT_INITIALIZED";
267 break;
268 default:
269 s = "other EGL error";
270 }
271 _eglLog(_EGL_DEBUG, "EGL user error 0x%x (%s) in %s\n", errCode, s, msg);
272 }
273
274 return EGL_FALSE;
275 }
276
277 EGLBoolean
_eglError(EGLint errCode,const char * msg)278 _eglError(EGLint errCode, const char *msg)
279 {
280 if (errCode != EGL_SUCCESS) {
281 EGLint type;
282 if (errCode == EGL_BAD_ALLOC)
283 type = EGL_DEBUG_MSG_CRITICAL_KHR;
284 else
285 type = EGL_DEBUG_MSG_ERROR_KHR;
286
287 _eglDebugReport(errCode, NULL, type, msg);
288 } else
289 _eglInternalError(errCode, msg);
290
291 return EGL_FALSE;
292 }
293
294 void
_eglDebugReport(EGLenum error,const char * funcName,EGLint type,const char * message,...)295 _eglDebugReport(EGLenum error, const char *funcName,
296 EGLint type, const char *message, ...)
297 {
298 _EGLThreadInfo *thr = _eglGetCurrentThread();
299 EGLDEBUGPROCKHR callback = NULL;
300 va_list args;
301
302 if (funcName == NULL)
303 funcName = thr->CurrentFuncName;
304
305 mtx_lock(_eglGlobal.Mutex);
306 if (_eglGlobal.debugTypesEnabled & DebugBitFromType(type))
307 callback = _eglGlobal.debugCallback;
308
309 mtx_unlock(_eglGlobal.Mutex);
310
311 char *message_buf = NULL;
312 if (message != NULL) {
313 va_start(args, message);
314 if (vasprintf(&message_buf, message, args) < 0)
315 message_buf = NULL;
316 va_end(args);
317 }
318
319 if (callback != NULL) {
320 callback(error, funcName, type, thr->Label, thr->CurrentObjectLabel,
321 message_buf);
322 }
323
324 if (type == EGL_DEBUG_MSG_CRITICAL_KHR || type == EGL_DEBUG_MSG_ERROR_KHR) {
325 char *func_message_buf = NULL;
326 /* Note: _eglError() is often called with msg == thr->currentFuncName */
327 if (message_buf && funcName && strcmp(message_buf, funcName) != 0) {
328 if (asprintf(&func_message_buf, "%s: %s", funcName, message_buf) < 0)
329 func_message_buf = NULL;
330 }
331 _eglInternalError(error, func_message_buf ? func_message_buf : funcName);
332 free(func_message_buf);
333 }
334 free(message_buf);
335 }
336