eglcurrent.c revision b8e80941
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 */
41static _EGLThreadInfo dummy_thread;
42static mtx_t _egl_TSDMutex = _MTX_INITIALIZER_NP;
43static EGLBoolean _egl_TSDInitialized;
44static tss_t _egl_TSD;
45static void _eglDestroyThreadInfo(_EGLThreadInfo *t);
46
47#ifdef GLX_USE_TLS
48static __thread const _EGLThreadInfo *_egl_TLS
49   __attribute__ ((tls_model("initial-exec")));
50#endif
51
52static 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
60static 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
69static 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
82static 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
103static void
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 */
115static _EGLThreadInfo *
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 */
130static void
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 */
141static inline _EGLThreadInfo *
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 *
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 */
176void
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 */
193EGLBoolean
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 *
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 */
215static EGLBoolean
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
277EGLBoolean
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
294void
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