1848b8605Smrg/* 2848b8605Smrg * Mesa 3-D graphics library 3848b8605Smrg * 4848b8605Smrg * Copyright (C) 1999-2008 Brian Paul All Rights Reserved. 5848b8605Smrg * 6848b8605Smrg * Permission is hereby granted, free of charge, to any person obtaining a 7848b8605Smrg * copy of this software and associated documentation files (the "Software"), 8848b8605Smrg * to deal in the Software without restriction, including without limitation 9848b8605Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10848b8605Smrg * and/or sell copies of the Software, and to permit persons to whom the 11848b8605Smrg * Software is furnished to do so, subject to the following conditions: 12848b8605Smrg * 13848b8605Smrg * The above copyright notice and this permission notice shall be included 14848b8605Smrg * in all copies or substantial portions of the Software. 15848b8605Smrg * 16848b8605Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17848b8605Smrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18848b8605Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19848b8605Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20848b8605Smrg * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21848b8605Smrg * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22848b8605Smrg * OTHER DEALINGS IN THE SOFTWARE. 23848b8605Smrg */ 24848b8605Smrg 25848b8605Smrg 26848b8605Smrg/* 27848b8605Smrg * This file manages the OpenGL API dispatch layer. 28848b8605Smrg * The dispatch table (struct _glapi_table) is basically just a list 29848b8605Smrg * of function pointers. 30848b8605Smrg * There are functions to set/get the current dispatch table for the 31848b8605Smrg * current thread and to manage registration/dispatch of dynamically 32848b8605Smrg * added extension functions. 33848b8605Smrg * 34848b8605Smrg * It's intended that this file and the other glapi*.[ch] files are 35848b8605Smrg * flexible enough to be reused in several places: XFree86, DRI- 36848b8605Smrg * based libGL.so, and perhaps the SGI SI. 37848b8605Smrg * 38848b8605Smrg * NOTE: There are no dependencies on Mesa in this code. 39848b8605Smrg * 40848b8605Smrg * Versions (API changes): 41848b8605Smrg * 2000/02/23 - original version for Mesa 3.3 and XFree86 4.0 42848b8605Smrg * 2001/01/16 - added dispatch override feature for Mesa 3.5 43848b8605Smrg * 2002/06/28 - added _glapi_set_warning_func(), Mesa 4.1. 44848b8605Smrg * 2002/10/01 - _glapi_get_proc_address() will now generate new entrypoints 45848b8605Smrg * itself (using offset ~0). _glapi_add_entrypoint() can be 46848b8605Smrg * called afterward and it'll fill in the correct dispatch 47848b8605Smrg * offset. This allows DRI libGL to avoid probing for DRI 48848b8605Smrg * drivers! No changes to the public glapi interface. 49848b8605Smrg */ 50848b8605Smrg 51b8e80941Smrg#include "c11/threads.h" 52848b8605Smrg#include "u_current.h" 53848b8605Smrg 54848b8605Smrg#ifndef MAPI_MODE_UTIL 55848b8605Smrg 56848b8605Smrg#include "table.h" 57848b8605Smrg#include "stub.h" 58848b8605Smrg 59848b8605Smrg#else 60848b8605Smrg 61848b8605Smrgextern void init_glapi_relocs_once(void); 62848b8605Smrgextern void (*__glapi_noop_table[])(void); 63848b8605Smrg 64848b8605Smrg#define table_noop_array __glapi_noop_table 65848b8605Smrg#define stub_init_once() init_glapi_relocs_once() 66848b8605Smrg 67848b8605Smrg#endif 68848b8605Smrg 69848b8605Smrg/** 70848b8605Smrg * \name Current dispatch and current context control variables 71848b8605Smrg * 72848b8605Smrg * Depending on whether or not multithreading is support, and the type of 73848b8605Smrg * support available, several variables are used to store the current context 74848b8605Smrg * pointer and the current dispatch table pointer. In the non-threaded case, 75848b8605Smrg * the variables \c _glapi_Dispatch and \c _glapi_Context are used for this 76848b8605Smrg * purpose. 77848b8605Smrg * 78848b8605Smrg * In the "normal" threaded case, the variables \c _glapi_Dispatch and 79848b8605Smrg * \c _glapi_Context will be \c NULL if an application is detected as being 80848b8605Smrg * multithreaded. Single-threaded applications will use \c _glapi_Dispatch 81848b8605Smrg * and \c _glapi_Context just like the case without any threading support. 82848b8605Smrg * When \c _glapi_Dispatch and \c _glapi_Context are \c NULL, the thread state 83848b8605Smrg * data \c _gl_DispatchTSD and \c ContextTSD are used. Drivers and the 84848b8605Smrg * static dispatch functions access these variables via \c _glapi_get_dispatch 85848b8605Smrg * and \c _glapi_get_context. 86848b8605Smrg * 87848b8605Smrg * There is a race condition in setting \c _glapi_Dispatch to \c NULL. It is 88848b8605Smrg * possible for the original thread to be setting it at the same instant a new 89848b8605Smrg * thread, perhaps running on a different processor, is clearing it. Because 90848b8605Smrg * of that, \c ThreadSafe, which can only ever be changed to \c GL_TRUE, is 91848b8605Smrg * used to determine whether or not the application is multithreaded. 92848b8605Smrg * 93848b8605Smrg * In the TLS case, the variables \c _glapi_Dispatch and \c _glapi_Context are 94848b8605Smrg * hardcoded to \c NULL. Instead the TLS variables \c _glapi_tls_Dispatch and 95848b8605Smrg * \c _glapi_tls_Context are used. Having \c _glapi_Dispatch and 96848b8605Smrg * \c _glapi_Context be hardcoded to \c NULL maintains binary compatability 97848b8605Smrg * between TLS enabled loaders and non-TLS DRI drivers. 98848b8605Smrg */ 99848b8605Smrg/*@{*/ 100848b8605Smrg#if defined(GLX_USE_TLS) 101848b8605Smrg 102b8e80941Smrg__thread struct _glapi_table *u_current_table 103848b8605Smrg __attribute__((tls_model("initial-exec"))) 104b8e80941Smrg#if defined(__NetBSD__) 105b8e80941Smrg = NULL; /* non-zero initializers not supported with dlopen */ 106b8e80941Smrg#else 107b8e80941Smrg = (struct _glapi_table *) table_noop_array; 108b8e80941Smrg#endif 109848b8605Smrg 110848b8605Smrg__thread void *u_current_context 111848b8605Smrg __attribute__((tls_model("initial-exec"))); 112848b8605Smrg 113848b8605Smrg#else 114848b8605Smrg 115b8e80941Smrgstruct _glapi_table *u_current_table = 116b8e80941Smrg (struct _glapi_table *) table_noop_array; 117848b8605Smrgvoid *u_current_context; 118848b8605Smrg 119b8e80941Smrgtss_t u_current_table_tsd; 120b8e80941Smrgstatic tss_t u_current_context_tsd; 121848b8605Smrgstatic int ThreadSafe; 122848b8605Smrg 123848b8605Smrg#endif /* defined(GLX_USE_TLS) */ 124848b8605Smrg/*@}*/ 125848b8605Smrg 126848b8605Smrg 127848b8605Smrgvoid 128848b8605Smrgu_current_destroy(void) 129848b8605Smrg{ 130b8e80941Smrg#if !defined(GLX_USE_TLS) 131b8e80941Smrg tss_delete(u_current_table_tsd); 132b8e80941Smrg tss_delete(u_current_context_tsd); 133848b8605Smrg#endif 134848b8605Smrg} 135848b8605Smrg 136848b8605Smrg 137b8e80941Smrg#if !defined(GLX_USE_TLS) 138848b8605Smrg 139848b8605Smrgstatic void 140848b8605Smrgu_current_init_tsd(void) 141848b8605Smrg{ 142b8e80941Smrg tss_create(&u_current_table_tsd, NULL); 143b8e80941Smrg tss_create(&u_current_context_tsd, NULL); 144848b8605Smrg} 145848b8605Smrg 146848b8605Smrg/** 147848b8605Smrg * Mutex for multithread check. 148848b8605Smrg */ 149848b8605Smrgstatic mtx_t ThreadCheckMutex = _MTX_INITIALIZER_NP; 150848b8605Smrg 151b8e80941Smrg 152b8e80941Smrg#ifdef _WIN32 153b8e80941Smrgtypedef DWORD thread_id; 154b8e80941Smrg#else 155b8e80941Smrgtypedef thrd_t thread_id; 156b8e80941Smrg#endif 157b8e80941Smrg 158b8e80941Smrg 159b8e80941Smrgstatic inline thread_id 160b8e80941Smrgget_thread_id(void) 161b8e80941Smrg{ 162b8e80941Smrg /* 163b8e80941Smrg * XXX: Callers of of this function assume it is a lightweight function. 164b8e80941Smrg * But unfortunately C11's thrd_current() gives no such guarantees. In 165b8e80941Smrg * fact, it's pretty hard to have a compliant implementation of 166b8e80941Smrg * thrd_current() on Windows with such characteristics. So for now, we 167b8e80941Smrg * side-step this mess and use Windows thread primitives directly here. 168b8e80941Smrg */ 169b8e80941Smrg#ifdef _WIN32 170b8e80941Smrg return GetCurrentThreadId(); 171b8e80941Smrg#else 172b8e80941Smrg return thrd_current(); 173b8e80941Smrg#endif 174b8e80941Smrg} 175b8e80941Smrg 176b8e80941Smrg 177b8e80941Smrgstatic inline int 178b8e80941Smrgthread_id_equal(thread_id t1, thread_id t2) 179b8e80941Smrg{ 180b8e80941Smrg#ifdef _WIN32 181b8e80941Smrg return t1 == t2; 182b8e80941Smrg#else 183b8e80941Smrg return thrd_equal(t1, t2); 184b8e80941Smrg#endif 185b8e80941Smrg} 186b8e80941Smrg 187b8e80941Smrg 188848b8605Smrg/** 189848b8605Smrg * We should call this periodically from a function such as glXMakeCurrent 190848b8605Smrg * in order to test if multiple threads are being used. 191848b8605Smrg */ 192848b8605Smrgvoid 193848b8605Smrgu_current_init(void) 194848b8605Smrg{ 195b8e80941Smrg static thread_id knownID; 196848b8605Smrg static int firstCall = 1; 197848b8605Smrg 198848b8605Smrg if (ThreadSafe) 199848b8605Smrg return; 200848b8605Smrg 201848b8605Smrg mtx_lock(&ThreadCheckMutex); 202848b8605Smrg if (firstCall) { 203848b8605Smrg u_current_init_tsd(); 204848b8605Smrg 205b8e80941Smrg knownID = get_thread_id(); 206848b8605Smrg firstCall = 0; 207848b8605Smrg } 208b8e80941Smrg else if (!thread_id_equal(knownID, get_thread_id())) { 209848b8605Smrg ThreadSafe = 1; 210848b8605Smrg u_current_set_table(NULL); 211848b8605Smrg u_current_set_context(NULL); 212848b8605Smrg } 213848b8605Smrg mtx_unlock(&ThreadCheckMutex); 214848b8605Smrg} 215848b8605Smrg 216848b8605Smrg#else 217848b8605Smrg 218848b8605Smrgvoid 219848b8605Smrgu_current_init(void) 220848b8605Smrg{ 221848b8605Smrg} 222848b8605Smrg 223848b8605Smrg#endif 224848b8605Smrg 225848b8605Smrg 226848b8605Smrg 227848b8605Smrg/** 228848b8605Smrg * Set the current context pointer for this thread. 229848b8605Smrg * The context pointer is an opaque type which should be cast to 230848b8605Smrg * void from the real context pointer type. 231848b8605Smrg */ 232848b8605Smrgvoid 233848b8605Smrgu_current_set_context(const void *ptr) 234848b8605Smrg{ 235848b8605Smrg u_current_init(); 236848b8605Smrg 237848b8605Smrg#if defined(GLX_USE_TLS) 238848b8605Smrg u_current_context = (void *) ptr; 239848b8605Smrg#else 240b8e80941Smrg tss_set(u_current_context_tsd, (void *) ptr); 241b8e80941Smrg u_current_context = (ThreadSafe) ? NULL : (void *) ptr; 242848b8605Smrg#endif 243848b8605Smrg} 244848b8605Smrg 245848b8605Smrg/** 246848b8605Smrg * Get the current context pointer for this thread. 247848b8605Smrg * The context pointer is an opaque type which should be cast from 248848b8605Smrg * void to the real context pointer type. 249848b8605Smrg */ 250848b8605Smrgvoid * 251848b8605Smrgu_current_get_context_internal(void) 252848b8605Smrg{ 253848b8605Smrg#if defined(GLX_USE_TLS) 254848b8605Smrg return u_current_context; 255848b8605Smrg#else 256b8e80941Smrg return ThreadSafe ? tss_get(u_current_context_tsd) : u_current_context; 257848b8605Smrg#endif 258848b8605Smrg} 259848b8605Smrg 260848b8605Smrg/** 261848b8605Smrg * Set the global or per-thread dispatch table pointer. 262848b8605Smrg * If the dispatch parameter is NULL we'll plug in the no-op dispatch 263848b8605Smrg * table (__glapi_noop_table). 264848b8605Smrg */ 265848b8605Smrgvoid 266b8e80941Smrgu_current_set_table(const struct _glapi_table *tbl) 267848b8605Smrg{ 268848b8605Smrg u_current_init(); 269848b8605Smrg 270848b8605Smrg stub_init_once(); 271848b8605Smrg 272848b8605Smrg if (!tbl) 273b8e80941Smrg tbl = (const struct _glapi_table *) table_noop_array; 274848b8605Smrg 275848b8605Smrg#if defined(GLX_USE_TLS) 276b8e80941Smrg u_current_table = (struct _glapi_table *) tbl; 277848b8605Smrg#else 278b8e80941Smrg tss_set(u_current_table_tsd, (void *) tbl); 279b8e80941Smrg u_current_table = (ThreadSafe) ? NULL : (void *) tbl; 280848b8605Smrg#endif 281848b8605Smrg} 282848b8605Smrg 283848b8605Smrg/** 284848b8605Smrg * Return pointer to current dispatch table for calling thread. 285848b8605Smrg */ 286b8e80941Smrgstruct _glapi_table * 287848b8605Smrgu_current_get_table_internal(void) 288848b8605Smrg{ 289848b8605Smrg#if defined(GLX_USE_TLS) 290b8e80941Smrg# if defined(__NetBSD__) 291b8e80941Smrg return (likely(u_current_table) ? u_current_table : (struct _glapi_table *) table_noop_array); 292b8e80941Smrg# else 293848b8605Smrg return u_current_table; 294b8e80941Smrg# endif 295848b8605Smrg#else 296b8e80941Smrg if (ThreadSafe) 297b8e80941Smrg return (struct _glapi_table *) tss_get(u_current_table_tsd); 298b8e80941Smrg else 299b8e80941Smrg return (struct _glapi_table *) u_current_table; 300848b8605Smrg#endif 301848b8605Smrg} 302