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 */ 45static _EGLThreadInfo dummy_thread = _EGL_THREAD_INFO_INITIALIZER; 46static mtx_t _egl_TSDMutex = _MTX_INITIALIZER_NP; 47static EGLBoolean _egl_TSDInitialized; 48static tss_t _egl_TSD; 49static void (*_egl_FreeTSD)(_EGLThreadInfo *); 50 51#ifdef GLX_USE_TLS 52static __thread const _EGLThreadInfo *_egl_TLS 53 __attribute__ ((tls_model("initial-exec"))); 54#endif 55 56static 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 64static 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 73static 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 87static 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 109static void 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 */ 122static _EGLThreadInfo * 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 */ 137static void 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 */ 148static inline _EGLThreadInfo * 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 * 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 */ 183void 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 */ 200EGLBoolean 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 * 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 */ 222static EGLBoolean 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 284EGLBoolean 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 */ 305EGLLabelKHR 306_eglGetThreadLabel(void) 307{ 308 _EGLThreadInfo *t = _eglGetCurrentThread(); 309 return t->Label; 310} 311 312static void 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 341void 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 351void 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