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