1 /************************************************************************** 2 * 3 * Copyright 2009-2010 Chia-I Wu <olvaffe (at) 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 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 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 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 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 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 * 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 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 * 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 * 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 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 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 * 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 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 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 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