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