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