eglcurrent.c revision a2f28ceea22254f09ee37039eec873ccdf689e6c
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 <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 */
42static _EGLThreadInfo dummy_thread = _EGL_THREAD_INFO_INITIALIZER;
43
44
45#if HAVE_PTHREAD
46#include <pthread.h>
47
48static _EGL_DECLARE_MUTEX(_egl_TSDMutex);
49static EGLBoolean _egl_TSDInitialized;
50static pthread_key_t _egl_TSD;
51static void (*_egl_FreeTSD)(_EGLThreadInfo *);
52
53#ifdef GLX_USE_TLS
54static __thread const _EGLThreadInfo *_egl_TLS
55   __attribute__ ((tls_model("initial-exec")));
56#endif
57
58static 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
66static 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
75static 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
89static 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 */
112static const _EGLThreadInfo *_egl_TSD;
113static void (*_egl_FreeTSD)(_EGLThreadInfo *);
114
115static INLINE void _eglSetTSD(const _EGLThreadInfo *t)
116{
117   _egl_TSD = t;
118}
119
120static INLINE _EGLThreadInfo *_eglGetTSD(void)
121{
122   return (_EGLThreadInfo *) _egl_TSD;
123}
124
125static INLINE void _eglFiniTSD(void)
126{
127   if (_egl_FreeTSD && _egl_TSD)
128      _egl_FreeTSD((_EGLThreadInfo *) _egl_TSD);
129}
130
131static 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
143static 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 */
156static _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 */
171static 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 */
182static 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 */
217void
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 */
234EGLBoolean
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 */
245PUBLIC _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 */
267EGLBoolean
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