GLdispatch.c 24 KB
Newer Older
Brian Nguyen's avatar
Brian Nguyen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
 * Copyright (c) 2013, NVIDIA CORPORATION.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * unaltered in all copies or substantial portions of the Materials.
 * Any additions, deletions, or changes to the original source files
 * must be clearly indicated in accompanying documentation.
 *
 * If only executable code is distributed, then the accompanying
 * documentation must state that "this software is based in part on the
 * work of the Khronos Group."
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

#include <string.h>
#include <pthread.h>
32
#include <dlfcn.h>
Brian Nguyen's avatar
Brian Nguyen committed
33

34
35
#include "trace.h"
#include "glvnd_list.h"
Brian Nguyen's avatar
Brian Nguyen committed
36
#include "GLdispatch.h"
37
#include "GLdispatchPrivate.h"
38
#include "stub.h"
39
#include "glvnd_pthread.h"
40
#include "app_error_check.h"
41

42
43
44
45
46
47
/*
 * Global current dispatch table list. We need this to fix up all current
 * dispatch tables whenever GetProcAddress() is called on a new function.
 * Accesses to this need to be protected by the dispatch lock.
 */
static struct glvnd_list currentDispatchList;
48
49
50
51
52
53

/*
 * Number of clients using GLdispatch.
 */
static int clientRefcount;

54
55
56
57
58
/*
 * The number of current contexts that GLdispatch is aware of
 */
static int numCurrentContexts;

59
60
61
/**
 * Private data for each API state.
 */
62
typedef struct __GLdispatchThreadStatePrivateRec {
63
    /// A pointer back to the API state.
64
    __GLdispatchThreadState *threadState;
65
66
67
68
69
70

    /// ID of the current vendor for this state
    int vendorID;

    /// The current (high-level) __GLdispatch table
    __GLdispatchTable *dispatch;
71
} __GLdispatchThreadStatePrivate;
72

73
74
75
76
typedef struct __GLdispatchProcEntryRec {
    char *procName;

    // Cached offset of this dispatch entry, retrieved from
77
    // _glapi_get_proc_offset()
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
    int offset;

    // The generation in which this dispatch entry was defined.
    // Used to determine whether a given dispatch table needs to
    // be fixed up with the right function address at this offset.
    int generation;

    // List handle
    struct glvnd_list entry;
} __GLdispatchProcEntry;

/*
 * List of valid extension procs which have been assigned prototypes. At make
 * current time, if the new context's generation is out-of-date, we iterate
 * through this list and fix up the new context with entrypoints with a greater
 * generation number. Accesses to this need to be protected by the dispatch
 * lock.
 */
static struct glvnd_list extProcList;

/*
 * Monotonically increasing integer describing the most up-to-date "generation"
 * of the dispatch table. Used to determine if a given table needs fixup.
 * Accesses to this need to be protected by the dispatch lock.
 *
 * Note: wrapping is theoretically an issue here, but shouldn't happen in
 * practice as it requires calling GetProcAddress() on 2^31-1 unique functions.
 * We'll run out of dispatch stubs long before then.
 */
static int latestGeneration;

109
110
111
112
/*
 * Dispatch stub list for entrypoint rewriting.
 */
static struct glvnd_list dispatchStubList;
113
114
static int nextDispatchStubID = 1;
static int localDispatchStubId = -1;
115
116
117
118
119

/*
 * Track the latest generation of the dispatch stub list so that vendor
 * libraries can determine when their copies of the stub offsets need to
 * be updated.
120
121
122
123
124
 *
 * Note: wrapping is theoretically an issue here, but encountering this
 * situation would require loading and unloading an API library that registers
 * its entrypoints with GLdispatch 2^63-1 times, so it is unlikely to be an
 * issue in practice.
125
 */
126
static GLint64 dispatchStubListGeneration;
127

128
129
130
131
132
133
/*
 * Used when generating new vendor IDs for GLdispatch clients.  Valid vendor
 * IDs must be non-zero.
 */
static int firstUnusedVendorID = 1;

134
/**
135
 * The key used to store the __GLdispatchThreadState for the current thread.
136
137
138
 */
static glvnd_key_t threadContextKey;

139
static void SetCurrentThreadState(__GLdispatchThreadState *threadState);
140
static void ThreadDestroyed(void *data);
141
142
static int RegisterStubCallbacks(const __GLdispatchStubPatchCallbacks *callbacks);

143

144
145
146
147
148
149
150
151
152
153
154
155
/*
 * The vendor ID of the current "owner" of the entrypoint code.  0 if
 * we are using the default libglvnd stubs.
 */
static int stubOwnerVendorID;

/*
 * The current set of patch callbacks being used, or NULL if using the
 * default libglvnd entrypoints.
 */
static const __GLdispatchPatchCallbacks *stubCurrentPatchCb;

156
157
static glvnd_thread_t firstThreadId = GLVND_THREAD_NULL_INIT;
static int isMultiThreaded = 0;
158

159
160
/*
 * The dispatch lock. This should be taken around any code that manipulates the
161
 * above global variables or makes calls to _glapi_get_proc_offset() or
162
163
164
165
166
167
168
169
170
 * _glapi_get_proc_offset().
 */
struct {
    glvnd_mutex_t lock;
    int isLocked;
} dispatchLock = { GLVND_MUTEX_INITIALIZER, 0 };

static inline void LockDispatch(void)
{
171
    __glvndPthreadFuncs.mutex_lock(&dispatchLock.lock);
172
173
174
175
176
177
    dispatchLock.isLocked = 1;
}

static inline void UnlockDispatch(void)
{
    dispatchLock.isLocked = 0;
178
    __glvndPthreadFuncs.mutex_unlock(&dispatchLock.lock);
179
180
181
}

#define CheckDispatchLocked() assert(dispatchLock.isLocked)
Brian Nguyen's avatar
Brian Nguyen committed
182

183
184
185
186
187
int __glDispatchGetABIVersion(void)
{
    return GLDISPATCH_ABI_VERSION;
}

188
189
190
191
192
193
194
195
#if defined(USE_ATTRIBUTE_CONSTRUCTOR)
void __attribute__ ((constructor)) __glDispatchOnLoadInit(void)
#else
void _init(void)
#endif
{
    // Here, we only initialize the pthreads imports. Everything else we'll
    // deal with in __glDispatchInit.
196
    glvndSetupPthreads();
197
    glvndAppErrorCheckInit();
198
199
}

200
void __glDispatchInit(void)
Brian Nguyen's avatar
Brian Nguyen committed
201
{
202
    LockDispatch();
203

204
    if (clientRefcount == 0) {
205
206
        // Initialize the GLAPI layer.
        _glapi_init();
207
        __glvndPthreadFuncs.key_create(&threadContextKey, ThreadDestroyed);
208
209
210

        glvnd_list_init(&extProcList);
        glvnd_list_init(&currentDispatchList);
211
        glvnd_list_init(&dispatchStubList);
212

213
        // Register GLdispatch's static entrypoints for rewriting
214
        localDispatchStubId = RegisterStubCallbacks(stub_get_patch_callbacks());
215
216
    }

217
    clientRefcount++;
218
    UnlockDispatch();
219
220
}

221
222
223
224
225
226
227
228
229
230
int __glDispatchNewVendorID(void)
{
    int vendorID;

    LockDispatch();
    vendorID = firstUnusedVendorID++;
    UnlockDispatch();

    return vendorID;
}
231

232
233
234
235
236
static void noop_func(void)
{
    // nop
}

237
238
239
static void DispatchCurrentRef(__GLdispatchTable *dispatch)
{
    CheckDispatchLocked();
240
241
242
243
    dispatch->currentThreads++;
    if (dispatch->currentThreads == 1) {
        glvnd_list_add(&dispatch->entry, &currentDispatchList);
    }
244
245
246
247
248
}

static void DispatchCurrentUnref(__GLdispatchTable *dispatch)
{
    CheckDispatchLocked();
249
250
251
252
253
    dispatch->currentThreads--;
    if (dispatch->currentThreads == 0) {
        glvnd_list_del(&dispatch->entry);
    }
    assert(dispatch->currentThreads >= 0);
254
255
256
257
258
259
260
261
262
263
264
}

/*
 * Fix up a dispatch table. Calls to this function must be protected by the
 * dispatch lock.
 */
static void FixupDispatchTable(__GLdispatchTable *dispatch)
{
    DBG_PRINTF(20, "dispatch=%p\n", dispatch);
    CheckDispatchLocked();

265
    __GLdispatchProcEntry *curProc;
266
267
268
    void *procAddr;
    void **tbl = (void **)dispatch->table;

269
    /*
270
     * For each proc in the extProcList, compare its gen# against that of
271
272
     * the context. If greater, then fix up the dispatch table to contain
     * the right entrypoint.
273
274
     * XXX optimization: could we assume that the list is sorted by generation
     * number and hence early out once we reach gen# <= the context's?
275
     */
276
277
278
279
280
281
    glvnd_list_for_each_entry(curProc, &extProcList, entry) {
        if (curProc->generation > dispatch->generation) {
            assert(curProc->offset != -1);
            assert(curProc->procName);

            procAddr = (void*)(*dispatch->getProcAddress)(
282
283
                curProc->procName,
                dispatch->getProcAddressParam);
284
285
286
287
288
289

            tbl[curProc->offset] = procAddr ? procAddr : (void *)noop_func;
            DBG_PRINTF(20, "extProc procName=%s, addr=%p, noop=%p\n",
                       curProc->procName, procAddr, noop_func);
        }
    }
290

291
292
293
294
295
296
    dispatch->generation = latestGeneration;
}

static __GLdispatchProcEntry *FindProcInList(const char *procName,
                                             struct glvnd_list *list)
{
297
298
299
300
301
302
303
304
305
306
307
    DBG_PRINTF(20, "%s\n", procName);
    __GLdispatchProcEntry *curProc;
    CheckDispatchLocked();
    glvnd_list_for_each_entry(curProc, list, entry) {
        if (!strcmp(curProc->procName, procName)) {
            DBG_PRINTF(20, "yes\n");
            return curProc;
        }
    }

    DBG_PRINTF(20, "no\n");
308
    return NULL;
Brian Nguyen's avatar
Brian Nguyen committed
309
310
311
312
}

PUBLIC __GLdispatchProc __glDispatchGetProcAddress(const char *procName)
{
313
314
315
316
317
318
319
320
    _glapi_proc addr;

    /*
     * We need to lock the dispatch before calling into glapi in order to
     * prevent races when retrieving the entrypoint stub.
     */
    LockDispatch();

321
    addr = _glapi_get_proc_address(procName);
322
323
324

    DBG_PRINTF(20, "addr=%p\n", addr);
    if (addr) {
325
326
        if (!FindProcInList(procName, &extProcList))
        {
327
            __GLdispatchTable *curDispatch;
328
329
            __GLdispatchProcEntry *pEntry = malloc(sizeof(*pEntry));
            pEntry->procName = strdup(procName);
330
331
            pEntry->offset = _glapi_get_proc_offset(procName);
            assert(pEntry->offset >= 0);
332
333
334
335
336
337

            /*
             * Bump the latestGeneration, then assign it to this proc.
             */
            pEntry->generation = ++latestGeneration;

338
            glvnd_list_add(&pEntry->entry, &extProcList);
339
340

            /*
341
342
             * Fixup any current dispatch tables to contain the right pointer
             * to this proc.
343
             */
344
345
346
            glvnd_list_for_each_entry(curDispatch, &currentDispatchList, entry) {
                FixupDispatchTable(curDispatch);
            }
347
        }
348
349
350
351
    }
    UnlockDispatch();

    return addr;
Brian Nguyen's avatar
Brian Nguyen committed
352
353
}

354
355
PUBLIC __GLdispatchTable *__glDispatchCreateTable(
        __GLgetProcAddressCallback getProcAddress, void *param)
Brian Nguyen's avatar
Brian Nguyen committed
356
{
357
358
    __GLdispatchTable *dispatch = malloc(sizeof(__GLdispatchTable));

359
360
361
362
    dispatch->generation = 0;
    dispatch->currentThreads = 0;
    dispatch->table = NULL;

363
    dispatch->getProcAddress = getProcAddress;
364
    dispatch->getProcAddressParam = param;
365
366

    return dispatch;
Brian Nguyen's avatar
Brian Nguyen committed
367
368
369
370
}

PUBLIC void __glDispatchDestroyTable(__GLdispatchTable *dispatch)
{
Brian Nguyen's avatar
Brian Nguyen committed
371
372
373
374
375
376
    /*
     * XXX: Technically, dispatch->currentThreads should be 0 if we're calling
     * into this function, but buggy apps may unload libGLX without losing
     * current, in which case this won't be true when the dispatch table
     * is destroyed.
     */
377
    LockDispatch();
378
    free(dispatch->table);
379
380
    free(dispatch);
    UnlockDispatch();
Brian Nguyen's avatar
Brian Nguyen committed
381
382
}

383
static struct _glapi_table
384
*CreateGLAPITable(__GLgetProcAddressCallback getProcAddress, void *param)
385
386
387
388
389
390
391
392
{
    size_t entries = _glapi_get_dispatch_table_size();
    struct _glapi_table *table = (struct _glapi_table *)
        calloc(1, entries * sizeof(void *));

    CheckDispatchLocked();

    if (table) {
393
394
        _glapi_init_table_from_callback(table,
                                        entries,
395
396
                                        getProcAddress,
                                        param);
397
398
399
400
401
    }

    return table;
}

402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
static int CurrentEntrypointsSafeToUse(int vendorID)
{
    CheckDispatchLocked();
    return !stubOwnerVendorID || (vendorID == stubOwnerVendorID);
}

static inline int PatchingIsDisabledByEnvVar(void)
{
    static GLboolean inited = GL_FALSE;
    static GLboolean disallowPatch = GL_FALSE;

    CheckDispatchLocked();

    if (!inited) {
        char *disallowPatchStr = getenv("__GLVND_DISALLOW_PATCHING");
        if (disallowPatchStr) {
            disallowPatch = atoi(disallowPatchStr);
419
420
421
422
423
        } else if (glvndAppErrorCheckGetEnabled()) {
            // Entrypoint rewriting means skipping the dispatch table in
            // libGLdispatch, which would disable checking for calling OpenGL
            // functions without a context.
            disallowPatch = GL_TRUE;
424
425
426
427
428
429
430
431
432
        }
        inited = GL_TRUE;
    }

    return disallowPatch;
}

static inline int ContextIsCurrentInAnyOtherThread(void)
{
433
    int thisThreadsContext = !!__glDispatchGetCurrentThreadState();
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
    int otherContexts;

    CheckDispatchLocked();

    otherContexts = (numCurrentContexts - thisThreadsContext);
    assert(otherContexts >= 0);

    return !!otherContexts;
}

static int PatchingIsSafe(void)
{
    CheckDispatchLocked();

    /*
     * Can only patch entrypoints on supported TLS access models
     */
451
    if (glvnd_list_is_empty(&dispatchStubList)) {
452
453
454
455
456
457
458
459
460
461
462
463
464
465
        return 0;
    }

    if (PatchingIsDisabledByEnvVar()) {
        return 0;
    }

    if (ContextIsCurrentInAnyOtherThread()) {
        return 0;
    }

    return 1;
}

466
typedef struct __GLdispatchStubCallbackRec {
467
468
    __GLdispatchStubPatchCallbacks callbacks;
    int id;
469
    GLboolean isPatched;
470
471
472
473

    struct glvnd_list entry;
} __GLdispatchStubCallback;

474
475
476
477
478
479
480
481
/**
 * Does the same thing as __glDispatchRegisterStubCallbacks, but requires the
 * caller to already be holding the dispatch lock.
 *
 * This is used in __glDispatchInit to register the libGLdispatch's own stub
 * functions.
 */
int RegisterStubCallbacks(const __GLdispatchStubPatchCallbacks *callbacks)
482
{
483
484
485
486
    if (callbacks == NULL) {
        return -1;
    }

487
    __GLdispatchStubCallback *stub = malloc(sizeof(*stub));
488
489
490
491
492
    if (stub == NULL) {
        return -1;
    }

    memcpy(&stub->callbacks, callbacks, sizeof(__GLdispatchStubPatchCallbacks));
493
    stub->isPatched = GL_FALSE;
494

495
    stub->id = nextDispatchStubID++;
496
497
    glvnd_list_add(&stub->entry, &dispatchStubList);
    dispatchStubListGeneration++;
498
499

    return stub->id;
500
501
}

502
503
504
505
506
507
508
509
510
int __glDispatchRegisterStubCallbacks(const __GLdispatchStubPatchCallbacks *callbacks)
{
    int ret;
    LockDispatch();
    ret = RegisterStubCallbacks(callbacks);
    UnlockDispatch();
    return ret;
}

511
void __glDispatchUnregisterStubCallbacks(int stubId)
512
513
{
    __GLdispatchStubCallback *curStub, *tmpStub;
514
515
516
517
    if (stubId < 0) {
        return;
    }

518
519
520
    LockDispatch();

    glvnd_list_for_each_entry_safe(curStub, tmpStub, &dispatchStubList, entry) {
521
        if (curStub->id == stubId) {
522
            glvnd_list_del(&curStub->entry);
523
524
            free(curStub);
            break;
525
526
527
        }
    }

528
    dispatchStubListGeneration++;
529
530
531
    UnlockDispatch();
}

Brian Nguyen's avatar
Brian Nguyen committed
532
533
534
535
536
537
538
539
540
541
542
543
544
void UnregisterAllStubCallbacks(void)
{
    __GLdispatchStubCallback *curStub, *tmpStub;
    CheckDispatchLocked();

    glvnd_list_for_each_entry_safe(curStub, tmpStub, &dispatchStubList, entry) {
        glvnd_list_del(&curStub->entry);
        free(curStub);
    }

    dispatchStubListGeneration++;
}

545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569

/*
 * Attempt to patch entrypoints with the given patch function and vendor ID.
 * If the function pointers are NULL, then this attempts to restore the default
 * libglvnd entrypoints.
 *
 * Returns 1 on success, 0 on failure.
 */
static int PatchEntrypoints(
   const __GLdispatchPatchCallbacks *patchCb,
   int vendorID
)
{
    __GLdispatchStubCallback *stub;
    CheckDispatchLocked();

    if (!PatchingIsSafe()) {
        return 0;
    }

    if (patchCb == stubCurrentPatchCb) {
        // Entrypoints already using the requested patch; no need to do anything
        return 1;
    }

570
571
572
    if (stubCurrentPatchCb) {
        // Notify the previous vendor that it no longer owns these
        // entrypoints.
573
574
575
        if (stubCurrentPatchCb->releasePatch != NULL) {
            stubCurrentPatchCb->releasePatch();
        }
576
577
578
579
580
581
582
583
584
585
586

        // Restore the stubs to the default implementation.
        glvnd_list_for_each_entry(stub, &dispatchStubList, entry) {
            if (stub->isPatched) {
                stub->callbacks.restoreFuncs();
                stub->isPatched = GL_FALSE;
            }
        }

        stubCurrentPatchCb = NULL;
        stubOwnerVendorID = 0;
587
588
    }

589
    if (patchCb) {
590
        GLboolean anySuccess = GL_FALSE;
591

592
        glvnd_list_for_each_entry(stub, &dispatchStubList, entry) {
593
            if (patchCb->isPatchSupported(stub->callbacks.getStubType(),
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
                        stub->callbacks.getStubSize()))
            {
                if (stub->callbacks.startPatch()) {
                    if (patchCb->initiatePatch(stub->callbacks.getStubType(),
                                stub->callbacks.getStubSize(),
                                stub->callbacks.getPatchOffset)) {
                        stub->callbacks.finishPatch();
                        stub->isPatched = GL_TRUE;
                        anySuccess = GL_TRUE;
                    } else {
                        stub->callbacks.abortPatch();
                        stub->isPatched = GL_FALSE;
                    }
                }
            } else if (stub->isPatched) {
                // The vendor library can't patch these stubs, but they were
                // patched before. Restore them now.
                stub->callbacks.restoreFuncs();
                stub->isPatched = GL_FALSE;
613
614
615
            }
        }

616
617
618
619
620
621
622
        if (anySuccess) {
            stubCurrentPatchCb = patchCb;
            stubOwnerVendorID = vendorID;
        } else {
            stubCurrentPatchCb = NULL;
            stubOwnerVendorID = 0;
        }
623
624
625
626
627
    }

    return 1;
}

628
PUBLIC GLboolean __glDispatchMakeCurrent(__GLdispatchThreadState *threadState,
629
                                         __GLdispatchTable *dispatch,
630
631
                                         int vendorID,
                                         const __GLdispatchPatchCallbacks *patchCb)
Brian Nguyen's avatar
Brian Nguyen committed
632
{
633
    __GLdispatchThreadStatePrivate *priv;
634

635
    if (__glDispatchGetCurrentThreadState() != NULL) {
636
637
638
        assert(!"__glDispatchMakeCurrent called with a current API state\n");
        return GL_FALSE;
    }
639

640
    priv = (__GLdispatchThreadStatePrivate *) malloc(sizeof(__GLdispatchThreadStatePrivate));
641
642
643
644
    if (priv == NULL) {
        return GL_FALSE;
    }

645
646
647
648
649
    // We need to fix up the dispatch table if it hasn't been
    // initialized, or there are new dynamic entries which were
    // added since the last time make current was called.
    LockDispatch();

650
651
652
653
654
655
    // Patch if necessary
    PatchEntrypoints(patchCb, vendorID);

    // If the current entrypoints are unsafe to use with this vendor, bail out.
    if (!CurrentEntrypointsSafeToUse(vendorID)) {
        UnlockDispatch();
656
        free(priv);
657
658
659
        return GL_FALSE;
    }

660
661
    if (!dispatch->table ||
        (dispatch->generation < latestGeneration)) {
662

663
664
        // Lazily create the dispatch table if we haven't already
        if (!dispatch->table) {
665
666
            dispatch->table = CreateGLAPITable(dispatch->getProcAddress,
                    dispatch->getProcAddressParam);
667
        }
668

669
670
671
        FixupDispatchTable(dispatch);
    }

672
673
    DispatchCurrentRef(dispatch);
    numCurrentContexts++;
674

675
676
    UnlockDispatch();

677
    /*
678
     * Update the API state with the new values.
679
     */
680
681
    priv->dispatch = dispatch;
    priv->vendorID = vendorID;
682
683
    priv->threadState = threadState;
    threadState->priv = priv;
684

685
686
687
    /*
     * Set the current state in TLS.
     */
688
    SetCurrentThreadState(threadState);
689
    _glapi_set_current(dispatch->table);
690
691

    return GL_TRUE;
Brian Nguyen's avatar
Brian Nguyen committed
692
693
}

694
static void LoseCurrentInternal(__GLdispatchThreadState *curThreadState,
695
        GLboolean threadDestroyed)
Brian Nguyen's avatar
Brian Nguyen committed
696
{
Brian Nguyen's avatar
Brian Nguyen committed
697
    LockDispatch();
698
699
700
701
    // Note that we don't try to restore the default stubs here. Chances are,
    // the next MakeCurrent will be from the same vendor, and if we leave them
    // patched, then we won't have to go through the overhead of patching them
    // again.
Brian Nguyen's avatar
Brian Nguyen committed
702

703
    if (curThreadState) {
Brian Nguyen's avatar
Brian Nguyen committed
704
        numCurrentContexts--;
705
706
707
        if (curThreadState->priv != NULL) {
            if (curThreadState->priv->dispatch != NULL) {
                DispatchCurrentUnref(curThreadState->priv->dispatch);
708
709
            }

710
711
            free(curThreadState->priv);
            curThreadState->priv = NULL;
712
        }
713
    }
714
    UnlockDispatch();
715

716
    if (!threadDestroyed) {
717
        SetCurrentThreadState(NULL);
718
        _glapi_set_current(NULL);
719
720
721
722
723
    }
}

PUBLIC void __glDispatchLoseCurrent(void)
{
724
725
    __GLdispatchThreadState *curThreadState = __glDispatchGetCurrentThreadState();
    if (curThreadState == NULL) {
726
727
        return;
    }
728
    LoseCurrentInternal(curThreadState, GL_FALSE);
Brian Nguyen's avatar
Brian Nguyen committed
729
730
}

731
__GLdispatchThreadState *__glDispatchGetCurrentThreadState(void)
732
{
733
    return (__GLdispatchThreadState *) __glvndPthreadFuncs.getspecific(threadContextKey);
734
735
}

736
void SetCurrentThreadState(__GLdispatchThreadState *threadState)
737
{
738
    __glvndPthreadFuncs.setspecific(threadContextKey, threadState);
739
740
}

Brian Nguyen's avatar
Brian Nguyen committed
741
742
743
744
745
/*
 * Handles resetting GLdispatch state after a fork.
 */
void __glDispatchReset(void)
{
746
747
    __GLdispatchTable *cur, *tmp;

Brian Nguyen's avatar
Brian Nguyen committed
748
    /* Reset the dispatch lock */
749
    __glvndPthreadFuncs.mutex_init(&dispatchLock.lock, NULL);
Brian Nguyen's avatar
Brian Nguyen committed
750
751
    dispatchLock.isLocked = 0;

752
    LockDispatch();
Brian Nguyen's avatar
Brian Nguyen committed
753
754
755
    /*
     * Clear out the current dispatch list.
     */
756

Brian Nguyen's avatar
Brian Nguyen committed
757
758
759
760
    glvnd_list_for_each_entry_safe(cur, tmp, &currentDispatchList, entry) {
        cur->currentThreads = 0;
        glvnd_list_del(&cur->entry);
    }
761
762
    UnlockDispatch();

Brian Nguyen's avatar
Brian Nguyen committed
763
    /* Clear GLAPI TLS entries. */
764
    SetCurrentThreadState(NULL);
765
    _glapi_set_current(NULL);
Brian Nguyen's avatar
Brian Nguyen committed
766
}
Brian Nguyen's avatar
Brian Nguyen committed
767
768
769
770
771
772

/*
 * Handles cleanup on library unload.
 */
void __glDispatchFini(void)
{
773
774
775
776
777
778
779
    LockDispatch();

    if (clientRefcount <= 0) {
        assert(clientRefcount > 0);
        UnlockDispatch();
        return;
    }
Brian Nguyen's avatar
Brian Nguyen committed
780
781
782

    clientRefcount--;

783
784
    if (clientRefcount == 0) {
        __GLdispatchProcEntry *curProc, *tmpProc;
Brian Nguyen's avatar
Brian Nguyen committed
785

786
787
        /* This frees the dispatchStubList */
        UnregisterAllStubCallbacks();
Brian Nguyen's avatar
Brian Nguyen committed
788

789
790
791
792
793
        /* 
         * Before we get here, client libraries should
         * have cleared out the current dispatch list.
         */
        assert(glvnd_list_is_empty(&currentDispatchList));
Brian Nguyen's avatar
Brian Nguyen committed
794

795
796
797
798
799
800
801
802
803
        /*
         * Clear out the getProcAddress lists.
         */
        glvnd_list_for_each_entry_safe(curProc, tmpProc, &extProcList, entry) {
            glvnd_list_del(&curProc->entry);
            free(curProc->procName);
            free(curProc);
        }

804
        __glvndPthreadFuncs.key_delete(threadContextKey);
805

806
807
        // Clean up GLAPI thread state
        _glapi_destroy();
Brian Nguyen's avatar
Brian Nguyen committed
808
809
810
811
812
    }

    UnlockDispatch();
}

813
814
void __glDispatchCheckMultithreaded(void)
{
815
    if (!__glvndPthreadFuncs.is_singlethreaded)
816
    {
817
818
819
820
821
822
823
824
825
826
827
828
        // Check to see if the current thread has a dispatch table assigned to
        // it, and if it doesn't, then plug in the no-op table.
        // This is a partial workaround to broken applications that try to call
        // OpenGL functions without a current context, without adding any
        // additional overhead to the dispatch stubs themselves. As long as the
        // thread calls at least one GLX function first, any OpenGL calls will
        // go to the no-op stubs instead of crashing.
        if (_glapi_get_current() == NULL) {
            // Calling _glapi_set_current(NULL) will plug in the no-op table.
            _glapi_set_current(NULL);
        }

829
830
        LockDispatch();
        if (!isMultiThreaded) {
831
832
            glvnd_thread_t tid = __glvndPthreadFuncs.self();
            if (__glvndPthreadFuncs.equal(firstThreadId, GLVND_THREAD_NULL)) {
833
                firstThreadId = tid;
834
            } else if (!__glvndPthreadFuncs.equal(firstThreadId, tid)) {
835
836
837
838
839
                isMultiThreaded = 1;
                _glapi_set_multithread();
            }
        }
        UnlockDispatch();
840
841
842
843

        if (stubCurrentPatchCb != NULL && stubCurrentPatchCb->threadAttach != NULL) {
            stubCurrentPatchCb->threadAttach();
        }
844
845
    }
}
Brian Nguyen's avatar
Brian Nguyen committed
846

847
848
void ThreadDestroyed(void *data)
{
849
    if (data != NULL) {
850
851
        __GLdispatchThreadState *threadState = (__GLdispatchThreadState *) data;
        LoseCurrentInternal(threadState, GL_TRUE);
852

853
854
        if (threadState->threadDestroyedCallback != NULL) {
            threadState->threadDestroyedCallback(threadState);
855
856
        }
    }
857
858
}