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 /* This should be kept in sync with _eglInitThreadInfo() */
41 #define _EGL_THREAD_INFO_INITIALIZER \
42 { EGL_SUCCESS, NULL, EGL_OPENGL_ES_API, NULL, NULL, NULL }
43
44 /* a fallback thread info to guarantee that every thread always has one */
45 static _EGLThreadInfo dummy_thread = _EGL_THREAD_INFO_INITIALIZER;
46 static mtx_t _egl_TSDMutex = _MTX_INITIALIZER_NP;
47 static EGLBoolean _egl_TSDInitialized;
48 static tss_t _egl_TSD;
49 static void (*_egl_FreeTSD)(_EGLThreadInfo *);
50
51 #ifdef GLX_USE_TLS
52 static __thread const _EGLThreadInfo *_egl_TLS
53 __attribute__ ((tls_model("initial-exec")));
54 #endif
55
_eglSetTSD(const _EGLThreadInfo * t)56 static inline void _eglSetTSD(const _EGLThreadInfo *t)
57 {
58 tss_set(_egl_TSD, (void *) t);
59 #ifdef GLX_USE_TLS
60 _egl_TLS = t;
61 #endif
62 }
63
_eglGetTSD(void)64 static inline _EGLThreadInfo *_eglGetTSD(void)
65 {
66 #ifdef GLX_USE_TLS
67 return (_EGLThreadInfo *) _egl_TLS;
68 #else
69 return (_EGLThreadInfo *) tss_get(_egl_TSD);
70 #endif
71 }
72
_eglFiniTSD(void)73 static inline void _eglFiniTSD(void)
74 {
75 mtx_lock(&_egl_TSDMutex);
76 if (_egl_TSDInitialized) {
77 _EGLThreadInfo *t = _eglGetTSD();
78
79 _egl_TSDInitialized = EGL_FALSE;
80 if (t && _egl_FreeTSD)
81 _egl_FreeTSD((void *) t);
82 tss_delete(_egl_TSD);
83 }
84 mtx_unlock(&_egl_TSDMutex);
85 }
86
_eglInitTSD(void (* dtor)(_EGLThreadInfo *))87 static inline EGLBoolean _eglInitTSD(void (*dtor)(_EGLThreadInfo *))
88 {
89 if (!_egl_TSDInitialized) {
90 mtx_lock(&_egl_TSDMutex);
91
92 /* check again after acquiring lock */
93 if (!_egl_TSDInitialized) {
94 if (tss_create(&_egl_TSD, (void (*)(void *)) dtor) != thrd_success) {
95 mtx_unlock(&_egl_TSDMutex);
96 return EGL_FALSE;
97 }
98 _egl_FreeTSD = dtor;
99 _eglAddAtExitCall(_eglFiniTSD);
100 _egl_TSDInitialized = EGL_TRUE;
101 }
102
103 mtx_unlock(&_egl_TSDMutex);
104 }
105
106 return EGL_TRUE;
107 }
108
109 static void
_eglInitThreadInfo(_EGLThreadInfo * t)110 _eglInitThreadInfo(_EGLThreadInfo *t)
111 {
112 memset(t, 0, sizeof(*t));
113 t->LastError = EGL_SUCCESS;
114 /* default, per EGL spec */
115 t->CurrentAPI = EGL_OPENGL_ES_API;
116 }
117
118
119 /**
120 * Allocate and init a new _EGLThreadInfo object.
121 */
122 static _EGLThreadInfo *
_eglCreateThreadInfo(void)123 _eglCreateThreadInfo(void)
124 {
125 _EGLThreadInfo *t = calloc(1, sizeof(_EGLThreadInfo));
126 if (t)
127 _eglInitThreadInfo(t);
128 else
129 t = &dummy_thread;
130 return t;
131 }
132
133
134 /**
135 * Delete/free a _EGLThreadInfo object.
136 */
137 static void
_eglDestroyThreadInfo(_EGLThreadInfo * t)138 _eglDestroyThreadInfo(_EGLThreadInfo *t)
139 {
140 if (t != &dummy_thread)
141 free(t);
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(&_eglDestroyThreadInfo) != 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
295 _eglDebugReport(errCode, msg, type, NULL);
296 } else
297 _eglInternalError(errCode, msg);
298
299 return EGL_FALSE;
300 }
301
302 /**
303 * Returns the label set for the current thread.
304 */
305 EGLLabelKHR
_eglGetThreadLabel(void)306 _eglGetThreadLabel(void)
307 {
308 _EGLThreadInfo *t = _eglGetCurrentThread();
309 return t->Label;
310 }
311
312 static void
_eglDebugReportFullv(EGLenum error,const char * command,const char * funcName,EGLint type,EGLLabelKHR objectLabel,const char * message,va_list args)313 _eglDebugReportFullv(EGLenum error, const char *command, const char *funcName,
314 EGLint type, EGLLabelKHR objectLabel, const char *message, va_list args)
315 {
316 EGLDEBUGPROCKHR callback = NULL;
317
318 mtx_lock(_eglGlobal.Mutex);
319 if (_eglGlobal.debugTypesEnabled & DebugBitFromType(type)) {
320 callback = _eglGlobal.debugCallback;
321 }
322 mtx_unlock(_eglGlobal.Mutex);
323
324 if (callback != NULL) {
325 char *buf = NULL;
326
327 if (message != NULL) {
328 if (vasprintf(&buf, message, args) < 0) {
329 buf = NULL;
330 }
331 }
332 callback(error, command, type, _eglGetThreadLabel(), objectLabel, buf);
333 free(buf);
334 }
335
336 if (type == EGL_DEBUG_MSG_CRITICAL_KHR || type == EGL_DEBUG_MSG_ERROR_KHR) {
337 _eglInternalError(error, funcName);
338 }
339 }
340
341 void
_eglDebugReportFull(EGLenum error,const char * command,const char * funcName,EGLint type,EGLLabelKHR objectLabel,const char * message,...)342 _eglDebugReportFull(EGLenum error, const char *command, const char *funcName,
343 EGLint type, EGLLabelKHR objectLabel, const char *message, ...)
344 {
345 va_list args;
346 va_start(args, message);
347 _eglDebugReportFullv(error, command, funcName, type, objectLabel, message, args);
348 va_end(args);
349 }
350
351 void
_eglDebugReport(EGLenum error,const char * funcName,EGLint type,const char * message,...)352 _eglDebugReport(EGLenum error, const char *funcName,
353 EGLint type, const char *message, ...)
354 {
355 _EGLThreadInfo *thr = _eglGetCurrentThread();
356 va_list args;
357
358 if (funcName == NULL) {
359 funcName = thr->CurrentFuncName;
360 }
361
362 va_start(args, message);
363 _eglDebugReportFullv(error, thr->CurrentFuncName, funcName, type, thr->CurrentObjectLabel, message, args);
364 va_end(args);
365 }
366