Home | History | Annotate | Download | only in main
      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 <stdlib.h>
     30 #include <string.h>
     31 #include "egllog.h"
     32 #include "eglmutex.h"
     33 #include "eglcurrent.h"
     34 #include "eglglobals.h"
     35 
     36 
     37 /* This should be kept in sync with _eglInitThreadInfo() */
     38 #define _EGL_THREAD_INFO_INITIALIZER \
     39    { EGL_SUCCESS, { NULL }, 0 }
     40 
     41 /* a fallback thread info to guarantee that every thread always has one */
     42 static _EGLThreadInfo dummy_thread = _EGL_THREAD_INFO_INITIALIZER;
     43 
     44 
     45 #if HAVE_PTHREAD
     46 #include <pthread.h>
     47 
     48 static _EGL_DECLARE_MUTEX(_egl_TSDMutex);
     49 static EGLBoolean _egl_TSDInitialized;
     50 static pthread_key_t _egl_TSD;
     51 static void (*_egl_FreeTSD)(_EGLThreadInfo *);
     52 
     53 #ifdef GLX_USE_TLS
     54 static __thread const _EGLThreadInfo *_egl_TLS
     55    __attribute__ ((tls_model("initial-exec")));
     56 #endif
     57 
     58 static INLINE void _eglSetTSD(const _EGLThreadInfo *t)
     59 {
     60    pthread_setspecific(_egl_TSD, (const void *) t);
     61 #ifdef GLX_USE_TLS
     62    _egl_TLS = t;
     63 #endif
     64 }
     65 
     66 static INLINE _EGLThreadInfo *_eglGetTSD(void)
     67 {
     68 #ifdef GLX_USE_TLS
     69    return (_EGLThreadInfo *) _egl_TLS;
     70 #else
     71    return (_EGLThreadInfo *) pthread_getspecific(_egl_TSD);
     72 #endif
     73 }
     74 
     75 static INLINE void _eglFiniTSD(void)
     76 {
     77    _eglLockMutex(&_egl_TSDMutex);
     78    if (_egl_TSDInitialized) {
     79       _EGLThreadInfo *t = _eglGetTSD();
     80 
     81       _egl_TSDInitialized = EGL_FALSE;
     82       if (t && _egl_FreeTSD)
     83          _egl_FreeTSD((void *) t);
     84       pthread_key_delete(_egl_TSD);
     85    }
     86    _eglUnlockMutex(&_egl_TSDMutex);
     87 }
     88 
     89 static INLINE EGLBoolean _eglInitTSD(void (*dtor)(_EGLThreadInfo *))
     90 {
     91    if (!_egl_TSDInitialized) {
     92       _eglLockMutex(&_egl_TSDMutex);
     93 
     94       /* check again after acquiring lock */
     95       if (!_egl_TSDInitialized) {
     96          if (pthread_key_create(&_egl_TSD, (void (*)(void *)) dtor) != 0) {
     97             _eglUnlockMutex(&_egl_TSDMutex);
     98             return EGL_FALSE;
     99          }
    100          _egl_FreeTSD = dtor;
    101          _eglAddAtExitCall(_eglFiniTSD);
    102          _egl_TSDInitialized = EGL_TRUE;
    103       }
    104 
    105       _eglUnlockMutex(&_egl_TSDMutex);
    106    }
    107 
    108    return EGL_TRUE;
    109 }
    110 
    111 #else /* HAVE_PTHREAD */
    112 static const _EGLThreadInfo *_egl_TSD;
    113 static void (*_egl_FreeTSD)(_EGLThreadInfo *);
    114 
    115 static INLINE void _eglSetTSD(const _EGLThreadInfo *t)
    116 {
    117    _egl_TSD = t;
    118 }
    119 
    120 static INLINE _EGLThreadInfo *_eglGetTSD(void)
    121 {
    122    return (_EGLThreadInfo *) _egl_TSD;
    123 }
    124 
    125 static INLINE void _eglFiniTSD(void)
    126 {
    127    if (_egl_FreeTSD && _egl_TSD)
    128       _egl_FreeTSD((_EGLThreadInfo *) _egl_TSD);
    129 }
    130 
    131 static INLINE EGLBoolean _eglInitTSD(void (*dtor)(_EGLThreadInfo *))
    132 {
    133    if (!_egl_FreeTSD && dtor) {
    134       _egl_FreeTSD = dtor;
    135       _eglAddAtExitCall(_eglFiniTSD);
    136    }
    137    return EGL_TRUE;
    138 }
    139 
    140 #endif /* !HAVE_PTHREAD */
    141 
    142 
    143 static void
    144 _eglInitThreadInfo(_EGLThreadInfo *t)
    145 {
    146    memset(t, 0, sizeof(*t));
    147    t->LastError = EGL_SUCCESS;
    148    /* default, per EGL spec */
    149    t->CurrentAPIIndex = _eglConvertApiToIndex(EGL_OPENGL_ES_API);
    150 }
    151 
    152 
    153 /**
    154  * Allocate and init a new _EGLThreadInfo object.
    155  */
    156 static _EGLThreadInfo *
    157 _eglCreateThreadInfo(void)
    158 {
    159    _EGLThreadInfo *t = (_EGLThreadInfo *) calloc(1, sizeof(_EGLThreadInfo));
    160    if (t)
    161       _eglInitThreadInfo(t);
    162    else
    163       t = &dummy_thread;
    164    return t;
    165 }
    166 
    167 
    168 /**
    169  * Delete/free a _EGLThreadInfo object.
    170  */
    171 static void
    172 _eglDestroyThreadInfo(_EGLThreadInfo *t)
    173 {
    174    if (t != &dummy_thread)
    175       free(t);
    176 }
    177 
    178 
    179 /**
    180  * Make sure TSD is initialized and return current value.
    181  */
    182 static INLINE _EGLThreadInfo *
    183 _eglCheckedGetTSD(void)
    184 {
    185    if (_eglInitTSD(&_eglDestroyThreadInfo) != EGL_TRUE) {
    186       _eglLog(_EGL_FATAL, "failed to initialize \"current\" system");
    187       return NULL;
    188    }
    189 
    190    return _eglGetTSD();
    191 }
    192 
    193 
    194 /**
    195  * Return the calling thread's thread info.
    196  * If the calling thread nevers calls this function before, or if its thread
    197  * info was destroyed, a new one is created.  This function never returns NULL.
    198  * In the case allocation fails, a dummy one is returned.  See also
    199  * _eglIsCurrentThreadDummy.
    200  */
    201 _EGLThreadInfo *
    202 _eglGetCurrentThread(void)
    203 {
    204    _EGLThreadInfo *t = _eglCheckedGetTSD();
    205    if (!t) {
    206       t = _eglCreateThreadInfo();
    207       _eglSetTSD(t);
    208    }
    209 
    210    return t;
    211 }
    212 
    213 
    214 /**
    215  * Destroy the calling thread's thread info.
    216  */
    217 void
    218 _eglDestroyCurrentThread(void)
    219 {
    220    _EGLThreadInfo *t = _eglCheckedGetTSD();
    221    if (t) {
    222       _eglDestroyThreadInfo(t);
    223       _eglSetTSD(NULL);
    224    }
    225 }
    226 
    227 
    228 /**
    229  * Return true if the calling thread's thread info is dummy.
    230  * A dummy thread info is shared by all threads and should not be modified.
    231  * Functions like eglBindAPI or eglMakeCurrent should check for dummy-ness
    232  * before updating the thread info.
    233  */
    234 EGLBoolean
    235 _eglIsCurrentThreadDummy(void)
    236 {
    237    _EGLThreadInfo *t = _eglCheckedGetTSD();
    238    return (!t || t == &dummy_thread);
    239 }
    240 
    241 
    242 /**
    243  * Return the currently bound context of the given API, or NULL.
    244  */
    245 PUBLIC _EGLContext *
    246 _eglGetAPIContext(EGLenum api)
    247 {
    248    _EGLThreadInfo *t = _eglGetCurrentThread();
    249    return t->CurrentContexts[_eglConvertApiToIndex(api)];
    250 }
    251 
    252 
    253 /**
    254  * Return the currently bound context of the current API, or NULL.
    255  */
    256 _EGLContext *
    257 _eglGetCurrentContext(void)
    258 {
    259    _EGLThreadInfo *t = _eglGetCurrentThread();
    260    return t->CurrentContexts[t->CurrentAPIIndex];
    261 }
    262 
    263 
    264 /**
    265  * Record EGL error code and return EGL_FALSE.
    266  */
    267 EGLBoolean
    268 _eglError(EGLint errCode, const char *msg)
    269 {
    270    _EGLThreadInfo *t = _eglGetCurrentThread();
    271 
    272    if (t == &dummy_thread)
    273       return EGL_FALSE;
    274 
    275    t->LastError = errCode;
    276 
    277    if (errCode != EGL_SUCCESS) {
    278       const char *s;
    279 
    280       switch (errCode) {
    281       case EGL_BAD_ACCESS:
    282          s = "EGL_BAD_ACCESS";
    283          break;
    284       case EGL_BAD_ALLOC:
    285          s = "EGL_BAD_ALLOC";
    286          break;
    287       case EGL_BAD_ATTRIBUTE:
    288          s = "EGL_BAD_ATTRIBUTE";
    289          break;
    290       case EGL_BAD_CONFIG:
    291          s = "EGL_BAD_CONFIG";
    292          break;
    293       case EGL_BAD_CONTEXT:
    294          s = "EGL_BAD_CONTEXT";
    295          break;
    296       case EGL_BAD_CURRENT_SURFACE:
    297          s = "EGL_BAD_CURRENT_SURFACE";
    298          break;
    299       case EGL_BAD_DISPLAY:
    300          s = "EGL_BAD_DISPLAY";
    301          break;
    302       case EGL_BAD_MATCH:
    303          s = "EGL_BAD_MATCH";
    304          break;
    305       case EGL_BAD_NATIVE_PIXMAP:
    306          s = "EGL_BAD_NATIVE_PIXMAP";
    307          break;
    308       case EGL_BAD_NATIVE_WINDOW:
    309          s = "EGL_BAD_NATIVE_WINDOW";
    310          break;
    311       case EGL_BAD_PARAMETER:
    312          s = "EGL_BAD_PARAMETER";
    313          break;
    314       case EGL_BAD_SURFACE:
    315          s = "EGL_BAD_SURFACE";
    316          break;
    317       case EGL_NOT_INITIALIZED:
    318          s = "EGL_NOT_INITIALIZED";
    319          break;
    320 #ifdef EGL_MESA_screen_surface
    321       case EGL_BAD_SCREEN_MESA:
    322          s = "EGL_BAD_SCREEN_MESA";
    323          break;
    324       case EGL_BAD_MODE_MESA:
    325          s = "EGL_BAD_MODE_MESA";
    326          break;
    327 #endif
    328       default:
    329          s = "other EGL error";
    330       }
    331       _eglLog(_EGL_DEBUG, "EGL user error 0x%x (%s) in %s\n", errCode, s, msg);
    332    }
    333 
    334    return EGL_FALSE;
    335 }
    336