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