Home | History | Annotate | Download | only in mapi
      1 /*
      2  * Mesa 3-D graphics library
      3  *
      4  * Copyright (C) 1999-2008  Brian Paul   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 "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included
     14  * in all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  */
     24 
     25 
     26 /*
     27  * This file manages the OpenGL API dispatch layer.
     28  * The dispatch table (struct _glapi_table) is basically just a list
     29  * of function pointers.
     30  * There are functions to set/get the current dispatch table for the
     31  * current thread and to manage registration/dispatch of dynamically
     32  * added extension functions.
     33  *
     34  * It's intended that this file and the other glapi*.[ch] files are
     35  * flexible enough to be reused in several places:  XFree86, DRI-
     36  * based libGL.so, and perhaps the SGI SI.
     37  *
     38  * NOTE: There are no dependencies on Mesa in this code.
     39  *
     40  * Versions (API changes):
     41  *   2000/02/23  - original version for Mesa 3.3 and XFree86 4.0
     42  *   2001/01/16  - added dispatch override feature for Mesa 3.5
     43  *   2002/06/28  - added _glapi_set_warning_func(), Mesa 4.1.
     44  *   2002/10/01  - _glapi_get_proc_address() will now generate new entrypoints
     45  *                 itself (using offset ~0).  _glapi_add_entrypoint() can be
     46  *                 called afterward and it'll fill in the correct dispatch
     47  *                 offset.  This allows DRI libGL to avoid probing for DRI
     48  *                 drivers!  No changes to the public glapi interface.
     49  */
     50 
     51 #include "c11/threads.h"
     52 #include "u_current.h"
     53 
     54 #ifndef MAPI_MODE_UTIL
     55 
     56 #include "table.h"
     57 #include "stub.h"
     58 
     59 #else
     60 
     61 extern void init_glapi_relocs_once(void);
     62 extern void (*__glapi_noop_table[])(void);
     63 
     64 #define table_noop_array __glapi_noop_table
     65 #define stub_init_once() init_glapi_relocs_once()
     66 
     67 #endif
     68 
     69 /**
     70  * \name Current dispatch and current context control variables
     71  *
     72  * Depending on whether or not multithreading is support, and the type of
     73  * support available, several variables are used to store the current context
     74  * pointer and the current dispatch table pointer.  In the non-threaded case,
     75  * the variables \c _glapi_Dispatch and \c _glapi_Context are used for this
     76  * purpose.
     77  *
     78  * In the "normal" threaded case, the variables \c _glapi_Dispatch and
     79  * \c _glapi_Context will be \c NULL if an application is detected as being
     80  * multithreaded.  Single-threaded applications will use \c _glapi_Dispatch
     81  * and \c _glapi_Context just like the case without any threading support.
     82  * When \c _glapi_Dispatch and \c _glapi_Context are \c NULL, the thread state
     83  * data \c _gl_DispatchTSD and \c ContextTSD are used.  Drivers and the
     84  * static dispatch functions access these variables via \c _glapi_get_dispatch
     85  * and \c _glapi_get_context.
     86  *
     87  * There is a race condition in setting \c _glapi_Dispatch to \c NULL.  It is
     88  * possible for the original thread to be setting it at the same instant a new
     89  * thread, perhaps running on a different processor, is clearing it.  Because
     90  * of that, \c ThreadSafe, which can only ever be changed to \c GL_TRUE, is
     91  * used to determine whether or not the application is multithreaded.
     92  *
     93  * In the TLS case, the variables \c _glapi_Dispatch and \c _glapi_Context are
     94  * hardcoded to \c NULL.  Instead the TLS variables \c _glapi_tls_Dispatch and
     95  * \c _glapi_tls_Context are used.  Having \c _glapi_Dispatch and
     96  * \c _glapi_Context be hardcoded to \c NULL maintains binary compatability
     97  * between TLS enabled loaders and non-TLS DRI drivers.
     98  */
     99 /*@{*/
    100 #if defined(GLX_USE_TLS)
    101 
    102 __thread struct _glapi_table *u_current_table
    103     __attribute__((tls_model("initial-exec")))
    104     = (struct _glapi_table *) table_noop_array;
    105 
    106 __thread void *u_current_context
    107     __attribute__((tls_model("initial-exec")));
    108 
    109 #else
    110 
    111 struct _glapi_table *u_current_table =
    112    (struct _glapi_table *) table_noop_array;
    113 void *u_current_context;
    114 
    115 tss_t u_current_table_tsd;
    116 static tss_t u_current_context_tsd;
    117 static int ThreadSafe;
    118 
    119 #endif /* defined(GLX_USE_TLS) */
    120 /*@}*/
    121 
    122 
    123 void
    124 u_current_destroy(void)
    125 {
    126 #if !defined(GLX_USE_TLS)
    127    tss_delete(u_current_table_tsd);
    128    tss_delete(u_current_context_tsd);
    129 #endif
    130 }
    131 
    132 
    133 #if !defined(GLX_USE_TLS)
    134 
    135 static void
    136 u_current_init_tsd(void)
    137 {
    138    tss_create(&u_current_table_tsd, NULL);
    139    tss_create(&u_current_context_tsd, NULL);
    140 }
    141 
    142 /**
    143  * Mutex for multithread check.
    144  */
    145 static mtx_t ThreadCheckMutex = _MTX_INITIALIZER_NP;
    146 
    147 
    148 #ifdef _WIN32
    149 typedef DWORD thread_id;
    150 #else
    151 typedef thrd_t thread_id;
    152 #endif
    153 
    154 
    155 static inline thread_id
    156 get_thread_id(void)
    157 {
    158    /*
    159     * XXX: Callers of of this function assume it is a lightweight function.
    160     * But unfortunately C11's thrd_current() gives no such guarantees.  In
    161     * fact, it's pretty hard to have a compliant implementation of
    162     * thrd_current() on Windows with such characteristics.  So for now, we
    163     * side-step this mess and use Windows thread primitives directly here.
    164     */
    165 #ifdef _WIN32
    166    return GetCurrentThreadId();
    167 #else
    168    return thrd_current();
    169 #endif
    170 }
    171 
    172 
    173 static inline int
    174 thread_id_equal(thread_id t1, thread_id t2)
    175 {
    176 #ifdef _WIN32
    177    return t1 == t2;
    178 #else
    179    return thrd_equal(t1, t2);
    180 #endif
    181 }
    182 
    183 
    184 /**
    185  * We should call this periodically from a function such as glXMakeCurrent
    186  * in order to test if multiple threads are being used.
    187  */
    188 void
    189 u_current_init(void)
    190 {
    191    static thread_id knownID;
    192    static int firstCall = 1;
    193 
    194    if (ThreadSafe)
    195       return;
    196 
    197    mtx_lock(&ThreadCheckMutex);
    198    if (firstCall) {
    199       u_current_init_tsd();
    200 
    201       knownID = get_thread_id();
    202       firstCall = 0;
    203    }
    204    else if (!thread_id_equal(knownID, get_thread_id())) {
    205       ThreadSafe = 1;
    206       u_current_set_table(NULL);
    207       u_current_set_context(NULL);
    208    }
    209    mtx_unlock(&ThreadCheckMutex);
    210 }
    211 
    212 #else
    213 
    214 void
    215 u_current_init(void)
    216 {
    217 }
    218 
    219 #endif
    220 
    221 
    222 
    223 /**
    224  * Set the current context pointer for this thread.
    225  * The context pointer is an opaque type which should be cast to
    226  * void from the real context pointer type.
    227  */
    228 void
    229 u_current_set_context(const void *ptr)
    230 {
    231    u_current_init();
    232 
    233 #if defined(GLX_USE_TLS)
    234    u_current_context = (void *) ptr;
    235 #else
    236    tss_set(u_current_context_tsd, (void *) ptr);
    237    u_current_context = (ThreadSafe) ? NULL : (void *) ptr;
    238 #endif
    239 }
    240 
    241 /**
    242  * Get the current context pointer for this thread.
    243  * The context pointer is an opaque type which should be cast from
    244  * void to the real context pointer type.
    245  */
    246 void *
    247 u_current_get_context_internal(void)
    248 {
    249 #if defined(GLX_USE_TLS)
    250    return u_current_context;
    251 #else
    252    return ThreadSafe ? tss_get(u_current_context_tsd) : u_current_context;
    253 #endif
    254 }
    255 
    256 /**
    257  * Set the global or per-thread dispatch table pointer.
    258  * If the dispatch parameter is NULL we'll plug in the no-op dispatch
    259  * table (__glapi_noop_table).
    260  */
    261 void
    262 u_current_set_table(const struct _glapi_table *tbl)
    263 {
    264    u_current_init();
    265 
    266    stub_init_once();
    267 
    268    if (!tbl)
    269       tbl = (const struct _glapi_table *) table_noop_array;
    270 
    271 #if defined(GLX_USE_TLS)
    272    u_current_table = (struct _glapi_table *) tbl;
    273 #else
    274    tss_set(u_current_table_tsd, (void *) tbl);
    275    u_current_table = (ThreadSafe) ? NULL : (void *) tbl;
    276 #endif
    277 }
    278 
    279 /**
    280  * Return pointer to current dispatch table for calling thread.
    281  */
    282 struct _glapi_table *
    283 u_current_get_table_internal(void)
    284 {
    285 #if defined(GLX_USE_TLS)
    286    return u_current_table;
    287 #else
    288    if (ThreadSafe)
    289       return (struct _glapi_table *) tss_get(u_current_table_tsd);
    290    else
    291       return (struct _glapi_table *) u_current_table;
    292 #endif
    293 }
    294