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 */
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
_eglSetTSD(const _EGLThreadInfo * t)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
_eglGetTSD(void)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
_eglFiniTSD(void)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
_eglInitTSD(void (* dtor)(_EGLThreadInfo *))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
_eglSetTSD(const _EGLThreadInfo * t)115 static INLINE void _eglSetTSD(const _EGLThreadInfo *t)
116 {
117 _egl_TSD = t;
118 }
119
_eglGetTSD(void)120 static INLINE _EGLThreadInfo *_eglGetTSD(void)
121 {
122 return (_EGLThreadInfo *) _egl_TSD;
123 }
124
_eglFiniTSD(void)125 static INLINE void _eglFiniTSD(void)
126 {
127 if (_egl_FreeTSD && _egl_TSD)
128 _egl_FreeTSD((_EGLThreadInfo *) _egl_TSD);
129 }
130
_eglInitTSD(void (* dtor)(_EGLThreadInfo *))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
_eglInitThreadInfo(_EGLThreadInfo * t)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 *
_eglCreateThreadInfo(void)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
_eglDestroyThreadInfo(_EGLThreadInfo * t)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 *
_eglCheckedGetTSD(void)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 *
_eglGetCurrentThread(void)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
_eglDestroyCurrentThread(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
_eglIsCurrentThreadDummy(void)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 *
_eglGetAPIContext(EGLenum api)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 *
_eglGetCurrentContext(void)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
_eglError(EGLint errCode,const char * msg)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