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