From f2ed2b87bae7d497d43ba26078b3bd6cbfb0babd Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 2 Oct 2014 13:39:09 -0700 Subject: [PATCH 01/13] Add rwlock_destroy and mutex_{init,destroy} imports Signed-off-by: Brian Nguyen --- src/util/glvnd_pthread/glvnd_pthread.c | 44 +++++++++++++++++++++++++- src/util/glvnd_pthread/glvnd_pthread.h | 5 +++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/util/glvnd_pthread/glvnd_pthread.c b/src/util/glvnd_pthread/glvnd_pthread.c index e72db86..2c66ce4 100644 --- a/src/util/glvnd_pthread/glvnd_pthread.c +++ b/src/util/glvnd_pthread/glvnd_pthread.c @@ -47,9 +47,13 @@ typedef struct GLVNDPthreadRealFuncsRec { int (*equal)(pthread_t t1, pthread_t t2); /* Locking primitives */ + int (*mutex_init)(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); + int (*mutex_destroy)(pthread_mutex_t *mutex); int (*mutex_lock)(pthread_mutex_t *mutex); int (*mutex_unlock)(pthread_mutex_t *mutex); + int (*rwlock_init)(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr); + int (*rwlock_destroy)(pthread_rwlock_t *rwlock); int (*rwlock_rdlock)(pthread_rwlock_t *rwlock); int (*rwlock_wrlock)(pthread_rwlock_t *rwlock); int (*rwlock_unlock)(pthread_rwlock_t *rwlock); @@ -124,10 +128,21 @@ static int st_equal(glvnd_thread_t t1, glvnd_thread_t t2) return 0; } +static int st_mutex_init(glvnd_mutex_t *mutex, const glvnd_mutexattr_t *attr) +{ + return 0; +} + +static int st_mutex_destroy(glvnd_mutex_t *mutex) +{ + return 0; +} + static int st_mutex_lock(glvnd_mutex_t *mutex) { return 0; } + static int st_mutex_unlock(glvnd_mutex_t *mutex) { return 0; @@ -138,6 +153,11 @@ int st_rwlock_init(glvnd_rwlock_t *rwlock, const glvnd_rwlockattr_t *attr) return 0; } +int st_rwlock_destroy(glvnd_rwlock_t *rwlock) +{ + return 0; +} + static int st_rwlock_rdlock(glvnd_rwlock_t *rwlock) { return 0; @@ -217,20 +237,36 @@ static int mt_equal(glvnd_thread_t t1, glvnd_thread_t t2) return pthreadRealFuncs.equal(t1.tid, t2.tid); } +static int mt_mutex_init(glvnd_mutex_t *mutex, const glvnd_mutexattr_t *attr) +{ + return pthreadRealFuncs.mutex_init(mutex, attr); +} + +static int mt_mutex_destroy(glvnd_mutex_t *mutex) +{ + return pthreadRealFuncs.mutex_destroy(mutex); +} + static int mt_mutex_lock(glvnd_mutex_t *mutex) { return pthreadRealFuncs.mutex_lock(mutex); } + static int mt_mutex_unlock(glvnd_mutex_t *mutex) { return pthreadRealFuncs.mutex_unlock(mutex); } -int mt_rwlock_init(glvnd_rwlock_t *rwlock, const glvnd_rwlockattr_t *attr) +static int mt_rwlock_init(glvnd_rwlock_t *rwlock, const glvnd_rwlockattr_t *attr) { return pthreadRealFuncs.rwlock_init(rwlock, attr); } +static int mt_rwlock_destroy(glvnd_rwlock_t *rwlock) +{ + return pthreadRealFuncs.rwlock_destroy(rwlock); +} + static int mt_rwlock_rdlock(glvnd_rwlock_t *rwlock) { return pthreadRealFuncs.rwlock_rdlock(rwlock); @@ -283,12 +319,15 @@ void glvndSetupPthreads(void *dlhandle, GLVNDPthreadFuncs *funcs) GET_MT_FUNC(funcs, dlhandle, join); GET_MT_FUNC(funcs, dlhandle, self); GET_MT_FUNC(funcs, dlhandle, equal); + GET_MT_FUNC(funcs, dlhandle, mutex_init); + GET_MT_FUNC(funcs, dlhandle, mutex_destroy); GET_MT_FUNC(funcs, dlhandle, mutex_lock); GET_MT_FUNC(funcs, dlhandle, mutex_unlock); // TODO: these can fall back on internal implementations // if they're not available in pthreads GET_MT_FUNC(funcs, dlhandle, rwlock_init); + GET_MT_FUNC(funcs, dlhandle, rwlock_destroy); GET_MT_FUNC(funcs, dlhandle, rwlock_rdlock); GET_MT_FUNC(funcs, dlhandle, rwlock_wrlock); GET_MT_FUNC(funcs, dlhandle, rwlock_unlock); @@ -312,9 +351,12 @@ fail: GET_ST_FUNC(funcs, join); GET_ST_FUNC(funcs, self); GET_ST_FUNC(funcs, equal); + GET_ST_FUNC(funcs, mutex_init); + GET_ST_FUNC(funcs, mutex_destroy); GET_ST_FUNC(funcs, mutex_lock); GET_ST_FUNC(funcs, mutex_unlock); GET_ST_FUNC(funcs, rwlock_init); + GET_ST_FUNC(funcs, rwlock_destroy); GET_ST_FUNC(funcs, rwlock_rdlock); GET_ST_FUNC(funcs, rwlock_wrlock); GET_ST_FUNC(funcs, rwlock_unlock); diff --git a/src/util/glvnd_pthread/glvnd_pthread.h b/src/util/glvnd_pthread/glvnd_pthread.h index 1c984ee..259f3ac 100644 --- a/src/util/glvnd_pthread/glvnd_pthread.h +++ b/src/util/glvnd_pthread/glvnd_pthread.h @@ -66,6 +66,7 @@ typedef struct _glvnd_thread_t { } glvnd_thread_t; typedef pthread_attr_t glvnd_thread_attr_t; +typedef pthread_mutexattr_t glvnd_mutexattr_t; typedef pthread_rwlockattr_t glvnd_rwlockattr_t; typedef pthread_key_t glvnd_key_t; @@ -87,9 +88,13 @@ typedef struct GLVNDPthreadFuncsRec { int (*equal)(glvnd_thread_t t1, glvnd_thread_t t2); /* Locking primitives */ + int (*mutex_init)(glvnd_mutex_t *mutex, const glvnd_mutexattr_t *attr); + int (*mutex_destroy)(glvnd_mutex_t *mutex); int (*mutex_lock)(glvnd_mutex_t *mutex); int (*mutex_unlock)(glvnd_mutex_t *mutex); + int (*rwlock_init)(glvnd_rwlock_t *rwlock, const glvnd_rwlockattr_t *attr); + int (*rwlock_destroy)(glvnd_rwlock_t *rwlock); int (*rwlock_rdlock)(glvnd_rwlock_t *rwlock); int (*rwlock_wrlock)(glvnd_rwlock_t *rwlock); int (*rwlock_unlock)(glvnd_rwlock_t *rwlock); -- GitLab From 077214e47857b62dcc87b55dd21bc022d7104296 Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 2 Oct 2014 13:53:30 -0700 Subject: [PATCH 02/13] Add XGLVUnregisterCloseDisplayCallbacks() This will be called by x11glvnd clients to clean up any installed callbacks on library teardown. Signed-off-by: Brian Nguyen --- src/x11glvnd/x11glvnd.h | 5 +++++ src/x11glvnd/x11glvndclient.c | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/x11glvnd/x11glvnd.h b/src/x11glvnd/x11glvnd.h index 165d276..9342884 100644 --- a/src/x11glvnd/x11glvnd.h +++ b/src/x11glvnd/x11glvnd.h @@ -67,4 +67,9 @@ char *XGLVQueryScreenVendorMapping( */ void XGLVRegisterCloseDisplayCallback(void (*callback)(Display *)); +/* + * Unregisters all registered callbacks. + */ +void XGLVUnregisterCloseDisplayCallbacks(void); + #endif // __X11GLVND_H__ diff --git a/src/x11glvnd/x11glvndclient.c b/src/x11glvnd/x11glvndclient.c index 9ae0134..a101636 100644 --- a/src/x11glvnd/x11glvndclient.c +++ b/src/x11glvnd/x11glvndclient.c @@ -90,6 +90,17 @@ void XGLVRegisterCloseDisplayCallback(void (*callback)(Display *)) glvnd_list_add(&hook->entry, &closeDisplayHookList); } +void XGLVUnregisterCloseDisplayCallbacks(void) +{ + CloseDisplayHook *curHook, *tmpHook; + glvnd_list_for_each_entry_safe(curHook, tmpHook, &closeDisplayHookList, entry) { + glvnd_list_del(&curHook->entry); + free(curHook); + } + assert(glvnd_list_is_empty(&closeDisplayHookList)); + closeDisplayHookListInitialized = False; +} + static XEXT_GENERATE_CLOSE_DISPLAY(close_display_internal, xglv_ext_info); static XEXT_CLOSE_DISPLAY_PROTO(close_display) -- GitLab From 6ed1294e200b07ecab3719e2fe2facac70f12fe0 Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 2 Oct 2014 15:43:37 -0700 Subject: [PATCH 03/13] Remove a duplicate declaration dispatchStubListGeneration was declared twice in GLdispatch.c, so delete one of the declarations. Signed-off-by: Brian Nguyen --- src/GLdispatch/GLdispatch.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/GLdispatch/GLdispatch.c b/src/GLdispatch/GLdispatch.c index eed5d6d..b2ec394 100644 --- a/src/GLdispatch/GLdispatch.c +++ b/src/GLdispatch/GLdispatch.c @@ -118,13 +118,6 @@ static int firstUnusedVendorID = 1; */ static struct glvnd_list dispatchStubList; -/* - * 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. - */ -static int dispatchStubListGeneration; - /* * The vendor ID of the current "owner" of the entrypoint code. 0 if * we are using the default libglvnd stubs. -- GitLab From 4ccb62cd1318b36954a9684079329177d8ad88c9 Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 2 Oct 2014 15:48:58 -0700 Subject: [PATCH 04/13] Track number of clients using GLdispatch Theoretically multiple clients (libGLX and a future libEGL) may call into __glDispatchInit() to set up dispatching, and we will want to refcount GLdispatch to determine when dispatch teardown should occur. So replace the global boolean 'initialized' with a counter 'clientRefcount' which tracks the number of clients using GLdispatch. Signed-off-by: Brian Nguyen --- src/GLdispatch/GLdispatch.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/GLdispatch/GLdispatch.c b/src/GLdispatch/GLdispatch.c index b2ec394..b3cf029 100644 --- a/src/GLdispatch/GLdispatch.c +++ b/src/GLdispatch/GLdispatch.c @@ -45,7 +45,15 @@ * Accesses to this need to be protected by the dispatch lock. */ static struct glvnd_list currentDispatchList; -static GLboolean initialized; + +/* + * Number of clients using GLdispatch. + */ +static int clientRefcount; + +/* + * Threading imports used for locking. + */ static GLVNDPthreadFuncs pthreadFuncs; /* @@ -157,10 +165,9 @@ static inline void UnlockDispatch(void) void __glDispatchInit(GLVNDPthreadFuncs *funcs) { - if (!initialized) { + if (clientRefcount == 0) { /* Initialize pthreads imports */ glvndSetupPthreads(RTLD_DEFAULT, &pthreadFuncs); - initialized = GL_TRUE; // Call into GLAPI to see if we are multithreaded // TODO: fix GLAPI to use the pthread funcs provided here? @@ -176,10 +183,10 @@ void __glDispatchInit(GLVNDPthreadFuncs *funcs) // Register GLdispatch's static entrypoints for rewriting __glDispatchRegisterStubCallbacks(stub_get_offsets, stub_restore); - - initialized = GL_TRUE; } + clientRefcount++; + if (funcs) { // If the client needs a copy of these funcs, assign them now // XXX: instead of copying, we should probably just provide a pointer. -- GitLab From 81523012da5a4ecc2a656a6ccaef0b596c401e0f Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 2 Oct 2014 15:52:16 -0700 Subject: [PATCH 05/13] Add __glDispatch{Fini,Reset}() __glDispatchFini() is called by a GLdispatch client to clean up GLdispatch-internal state when the client is done using GLdispatch functionality. __glDispatchReset() is called by a GLdispatch client to determine whether fork recovery is needed, and handle the fork if necessary. Signed-off-by: Brian Nguyen --- src/GLdispatch/GLdispatch.c | 107 +++++++++++++++++++++++++++++++++--- src/GLdispatch/GLdispatch.h | 11 ++++ 2 files changed, 111 insertions(+), 7 deletions(-) diff --git a/src/GLdispatch/GLdispatch.c b/src/GLdispatch/GLdispatch.c index b3cf029..d046ac0 100644 --- a/src/GLdispatch/GLdispatch.c +++ b/src/GLdispatch/GLdispatch.c @@ -409,9 +409,12 @@ PUBLIC __GLdispatchTable *__glDispatchCreateTable(__GLgetProcAddressCallback get PUBLIC void __glDispatchDestroyTable(__GLdispatchTable *dispatch) { - // NOTE this assumes the table is not current! - // TODO: delete the global lists - // TODO: this is currently unused... + /* + * 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. + */ LockDispatch(); free(dispatch->table); free(dispatch); @@ -537,6 +540,19 @@ void __glDispatchUnregisterStubCallbacks( UnlockDispatch(); } +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++; +} + /* * Attempt to patch entrypoints with the given patch function and vendor ID. @@ -639,7 +655,9 @@ PUBLIC GLboolean __glDispatchMakeCurrent(__GLdispatchAPIState *apiState, DispatchCurrentRef(dispatch); } - numCurrentContexts++; + if (!curApiState || !curApiState->context) { + numCurrentContexts++; + } UnlockDispatch(); @@ -665,6 +683,16 @@ PUBLIC void __glDispatchLoseCurrent(void) __GLdispatchAPIState *curApiState = (__GLdispatchAPIState *)_glapi_get_current(CURRENT_API_STATE); + + LockDispatch(); + // Try to restore the libglvnd default stubs, if possible. + PatchEntrypoints(NULL, 0); + + if (curApiState && curApiState->context) { + numCurrentContexts--; + } + UnlockDispatch(); + if (curApiState) { LockDispatch(); DispatchCurrentUnref(curApiState->dispatch); @@ -675,14 +703,79 @@ PUBLIC void __glDispatchLoseCurrent(void) curApiState->vendorID = -1; } + _glapi_set_current(NULL, CURRENT_API_STATE); + _glapi_set_current(NULL, CURRENT_CONTEXT); + _glapi_set_dispatch(NULL); +} + +/* + * Handles resetting GLdispatch state after a fork. + */ +void __glDispatchReset(void) +{ + /* Reset the dispatch lock */ + pthreadFuncs.mutex_init(&dispatchLock.lock, NULL); + dispatchLock.isLocked = 0; + LockDispatch(); - // Try to restore the libglvnd default stubs, if possible. - PatchEntrypoints(NULL, 0); + /* + * Clear out the current dispatch list. + */ + __GLdispatchTable *cur, *tmp; - numCurrentContexts--; + glvnd_list_for_each_entry_safe(cur, tmp, ¤tDispatchList, entry) { + cur->currentThreads = 0; + glvnd_list_del(&cur->entry); + } UnlockDispatch(); + /* Clear GLAPI TLS entries. */ _glapi_set_current(NULL, CURRENT_API_STATE); _glapi_set_current(NULL, CURRENT_CONTEXT); _glapi_set_dispatch(NULL); } + +/* + * Handles cleanup on library unload. + */ +void __glDispatchFini(void) +{ + __GLdispatchProcEntry *curProc, *tmpProc; + assert(clientRefcount > 0); + + clientRefcount--; + + LockDispatch(); + /* This frees the dispatchStubList */ + UnregisterAllStubCallbacks(); + + /* + * Before we get here, client libraries should + * have cleared out the current dispatch list. + */ + assert(glvnd_list_is_empty(¤tDispatchList)); + + /* + * Clear out the getProcAddress lists. + */ + glvnd_list_for_each_entry_safe(curProc, tmpProc, &newProcList, entry) { + glvnd_list_del(&curProc->entry); + free(curProc->procName); + free(curProc); + } + + glvnd_list_for_each_entry_safe(curProc, tmpProc, &extProcList, entry) { + // XXX: is there any glapi-specific cleanup that needs to happen + // here? + glvnd_list_del(&curProc->entry); + free(curProc->procName); + free(curProc); + } + + UnlockDispatch(); + + // Clean up GLAPI thread state + _glapi_destroy_multithread(); +} + + diff --git a/src/GLdispatch/GLdispatch.h b/src/GLdispatch/GLdispatch.h index 1ec9984..a88ed7e 100644 --- a/src/GLdispatch/GLdispatch.h +++ b/src/GLdispatch/GLdispatch.h @@ -117,6 +117,17 @@ typedef void (*__GLdispatchGetOffsetHook)(void *(*lookupStubOffset)(const char * */ PUBLIC void __glDispatchInit(GLVNDPthreadFuncs *funcs); +/*! + * Tears down GLdispatch state. + */ +PUBLIC void __glDispatchFini(void); + +/*! + * Called when the client library has detected a fork, and GLdispatch state + * needs to be reset to handle the fork. + */ +PUBLIC void __glDispatchReset(void); + /*! * This returns a process-unique ID that is suitable for use with a new GL * vendor. -- GitLab From b7b6e43b3a72174582a192d3ace77df946182816 Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 2 Oct 2014 15:58:47 -0700 Subject: [PATCH 06/13] Add LKDHASH_TEARDOWN() macro to handle hash table cleanup This macro frees all entries in a locked hash table, and either re-initializes the table's lock for fork recovery, or destroys the lock, in which case the hash table is fully torn down and must be re-initialized before it can be used. Signed-off-by: Brian Nguyen --- include/lkdhash.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/include/lkdhash.h b/include/lkdhash.h index e8349c5..3557515 100644 --- a/include/lkdhash.h +++ b/include/lkdhash.h @@ -40,4 +40,47 @@ */ #define _LH(_lockedhash) ((_lockedhash).hash) +#define LKDHASH_TEARDOWN_2(imp, _lockedhash, _param, _cur, _tmp, _cleanup) do { \ + LKDHASH_WRLOCK(imp, _lockedhash); \ + HASH_ITER(hh, _LH( _lockedhash), _cur, _tmp) { \ + HASH_DEL(_LH(_lockedhash), _cur); \ + if (_cleanup) { \ + _cleanup(_param, _cur); \ + } \ + free(_cur); \ + } \ + assert(!_LH(_lockedhash)); \ + LKDHASH_UNLOCK(imp, _lockedhash); \ +} while (0) + +/*! + * Macro for deleting all entries in a locked hash, as well as the protecting + * lock. Assumes that hash entries have been allocated using malloc(3) or + * similar and are safe to pass into free(3). + * + * _ht indicates the type of the hash table to use, and _lh indicates the + * hash table variable. + * + * _cleanup is a callback function which takes (_void *, _ht *) as arguments, + * or NULL. + * + * _param is an extra parmeter to pass into the callback function of type + * (void *). + * + * _reset indicates whether the lock needs to be re-initialized (for fork + * handling). + */ +#define LKDHASH_TEARDOWN(imp, _ht, _lh, _cleanup, _param, _reset) do { \ + _ht *cur ## _ht, *tmp ## _ht; \ + typedef void (*pfnCleanup ## _ht)(void *p, _ht *h); \ + pfnCleanup ## _ht pCleanup ## _ht = _cleanup; \ + LKDHASH_TEARDOWN_2(imp, _lh, _param, cur ## _ht, \ + tmp ## _ht, pCleanup ## _ht); \ + if (_reset) { \ + (imp).rwlock_init(&(_lh).lock, NULL); \ + } else { \ + (imp).rwlock_destroy(&(_lh).lock); \ + } \ +} while (0) + #endif -- GitLab From 27d91186e3a891b9e7f301ef7dec7c8b9db2ca4a Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 2 Oct 2014 16:03:55 -0700 Subject: [PATCH 07/13] Call into GLdispatch to lose current before a thread is destroyed Without this, the current context accounting in GLdispatch can get confused if a thread is destroyed while a context is still current to that thread. Signed-off-by: Brian Nguyen --- src/GLX/libglx.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/GLX/libglx.c b/src/GLX/libglx.c index a517403..4553bee 100644 --- a/src/GLX/libglx.c +++ b/src/GLX/libglx.c @@ -88,6 +88,13 @@ static void ThreadDestroyed(void *tsdCtx) LKDHASH_WRLOCK(__glXPthreadFuncs, __glXCurrentContextHash); UntrackCurrentContext(tsdCtx); LKDHASH_UNLOCK(__glXPthreadFuncs, __glXCurrentContextHash); + + /* + * Call into GLdispatch to lose current to this thread. XXX + * should we check for ownership of the API state before doing + * this? + */ + __glDispatchLoseCurrent(); } static void ThreadCreateTSDContextOnce(void) -- GitLab From f9b084b67e7780b3cc1deb43a23f64067390d585 Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 2 Oct 2014 16:07:54 -0700 Subject: [PATCH 08/13] Fix issues with {Track,Untrack}CurrentContext() - Don't clear the current TSD entry when UntrackCurrentContext() is called. The TSD entry already contains the correct current context when it is set by TrackCurrentContext() prior to the call to UntrackCurrentContext(). - In the comment above UntrackCurrentContext(), delete an obsolete TODO. - If make current fails, re-track the old context before returning. Signed-off-by: Brian Nguyen --- src/GLX/libglx.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/GLX/libglx.c b/src/GLX/libglx.c index 4553bee..13a60c2 100644 --- a/src/GLX/libglx.c +++ b/src/GLX/libglx.c @@ -312,8 +312,9 @@ void __glXNotifyContextDestroyed(GLXContext ctx) } /* - * Adds a context to the current context list. Note: __glXCurrentContextHash - * must be write-locked before calling this function! + * Adds a context to the current context list, and updates this thread's context + * TSD entry. Note: __glXCurrentContextHash must be write-locked before calling + * this function! * * Returns True on success. */ @@ -371,8 +372,7 @@ static Bool IsContextCurrentToAnyOtherThread(GLXContext ctx) /* * Removes a context from the current context list, and removes any context -> - * screen mappings if necessary. TODO: need to handle the corner case where the - * thread is terminated and we haven't lost current to this context + * screen mappings if necessary. * * Note: The __glXCurrentContextHash must be write-locked before calling this * function! @@ -394,9 +394,6 @@ static void UntrackCurrentContext(GLXContext ctx) HASH_DELETE(hh, _LH(__glXCurrentContextHash), pEntry); free(pEntry); - // Clear the TSD entry - __glXPthreadFuncs.setspecific(tsdContextKey, NULL); - if (needsUnmap) { __glXRemoveScreenContextMapping(ctx); } @@ -594,6 +591,8 @@ PUBLIC Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext contex UntrackCurrentContext(oldContext); } else { UntrackCurrentContext(context); + ret = TrackCurrentContext(oldContext); + assert(ret); } } -- GitLab From dc2f27980a885a266cf136e99bcdf41a1438918f Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 2 Oct 2014 16:17:05 -0700 Subject: [PATCH 09/13] Don't pass a NULL filename into dlopen(3). Signed-off-by: Brian Nguyen --- src/GLX/libglxmapping.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/GLX/libglxmapping.c b/src/GLX/libglxmapping.c index f6a4ce8..e6e9802 100644 --- a/src/GLX/libglxmapping.c +++ b/src/GLX/libglxmapping.c @@ -295,7 +295,6 @@ static char *ConstructVendorLibraryFilename(const char *vendorName) __GLXvendorInfo *__glXLookupVendorByName(const char *vendorName) { __GLXvendorNameHash *pEntry = NULL; - char *filename; void *dlhandle = NULL; __PFNGLXMAINPROC glxMainProc; const __GLXdispatchTableStatic *dispatch; @@ -318,6 +317,8 @@ __GLXvendorInfo *__glXLookupVendorByName(const char *vendorName) // Do another lookup to check uniqueness HASH_FIND(hh, _LH(__glXVendorNameHash), vendorName, strlen(vendorName), pEntry); if (!pEntry) { + char *filename; + // Previously unseen vendor. dlopen() the new vendor and add it to the // hash table. pEntry = calloc(1, sizeof(*pEntry)); @@ -326,7 +327,9 @@ __GLXvendorInfo *__glXLookupVendorByName(const char *vendorName) } filename = ConstructVendorLibraryFilename(vendorName); - dlhandle = dlopen(filename, RTLD_LAZY); + if (filename) { + dlhandle = dlopen(filename, RTLD_LAZY); + } free(filename); if (!dlhandle) { goto fail; -- GitLab From d39e01a307d09388a5b164ae682eedb7b8538b05 Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Thu, 2 Oct 2014 16:17:59 -0700 Subject: [PATCH 10/13] Implement fork-handling and library teardown paths - Define a function __glXThreadInitialize() which gets called when entering any GLX entrypoint, or when __glXGetDynDispatch() is called by a vendor-provided GLX extension entrypoint. This function detects if a fork occurred since the last entrypoint was called, and performs fork recovery if needed. - Implement __glXFini() so that libGLX cleans up allocated resources when it is unloaded. These paths make heavy use of the LKDHASH_TEARDOWN() macro that was implemented in a previous commit. Signed-off-by: Brian Nguyen --- src/GLX/libglx.c | 238 +++++++++++++++++++++++++++++++++++++++- src/GLX/libglxcurrent.h | 2 +- src/GLX/libglxmapping.c | 90 +++++++++++++-- src/GLX/libglxmapping.h | 5 +- src/GLX/libglxthread.h | 2 + 5 files changed, 323 insertions(+), 14 deletions(-) diff --git a/src/GLX/libglx.c b/src/GLX/libglx.c index 13a60c2..24400b7 100644 --- a/src/GLX/libglx.c +++ b/src/GLX/libglx.c @@ -35,6 +35,8 @@ #include #include #include +#include +#include #include "libglxthread.h" #include "libglxabipriv.h" @@ -107,6 +109,8 @@ static void ThreadCreateTSDContextOnce(void) PUBLIC XVisualInfo* glXChooseVisual(Display *dpy, int screen, int *attrib_list) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); return pDispatch->glx14ep.chooseVisual(dpy, screen, attrib_list); @@ -133,6 +137,8 @@ PUBLIC void glXCopyContext(Display *dpy, GLXContext src, GLXContext dst, PUBLIC GLXContext glXCreateContext(Display *dpy, XVisualInfo *vis, GLXContext share_list, Bool direct) { + __glXThreadInitialize(); + const int screen = vis->screen; const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -146,6 +152,8 @@ PUBLIC GLXContext glXCreateContext(Display *dpy, XVisualInfo *vis, PUBLIC void glXDestroyContext(Display *dpy, GLXContext context) { + __glXThreadInitialize(); + const int screen = __glXScreenFromContext(context); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -157,6 +165,8 @@ PUBLIC void glXDestroyContext(Display *dpy, GLXContext context) PUBLIC GLXPixmap glXCreateGLXPixmap(Display *dpy, XVisualInfo *vis, Pixmap pixmap) { + __glXThreadInitialize(); + const int screen = vis->screen; const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -170,6 +180,8 @@ PUBLIC GLXPixmap glXCreateGLXPixmap(Display *dpy, XVisualInfo *vis, Pixmap pixma PUBLIC void glXDestroyGLXPixmap(Display *dpy, GLXPixmap pix) { + __glXThreadInitialize(); + const int screen = __glXScreenFromDrawable(dpy, pix); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -181,6 +193,8 @@ PUBLIC void glXDestroyGLXPixmap(Display *dpy, GLXPixmap pix) PUBLIC int glXGetConfig(Display *dpy, XVisualInfo *vis, int attrib, int *value) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch; if (!dpy || !vis || !value) { @@ -194,12 +208,16 @@ PUBLIC int glXGetConfig(Display *dpy, XVisualInfo *vis, int attrib, int *value) PUBLIC GLXContext glXGetCurrentContext(void) { + __glXThreadInitialize(); + return __glXGetCurrentContext(); } PUBLIC GLXDrawable glXGetCurrentDrawable(void) { + __glXThreadInitialize(); + __GLXAPIState *apiState = __glXGetCurrentAPIState(); return apiState->currentDraw; } @@ -207,6 +225,8 @@ PUBLIC GLXDrawable glXGetCurrentDrawable(void) PUBLIC Bool glXIsDirect(Display *dpy, GLXContext context) { + __glXThreadInitialize(); + const int screen = __glXScreenFromContext(context); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -249,6 +269,12 @@ static __GLXAPIState *CreateAPIState(glvnd_thread_t tid) return apiState; } +/* NOTE this assumes the __glXAPIStateHash lock is taken! */ +static void CleanupAPIStateEntry(void *unused, __GLXAPIState *apiState) +{ + free(apiState->glas.id); +} + /* NOTE this assumes the __glXAPIStateHash lock is taken! */ static __GLXAPIState *LookupAPIState(glvnd_thread_t tid) { @@ -529,6 +555,8 @@ static void SaveCurrentValues(GLXDrawable *pDraw, PUBLIC Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext context) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch; Bool ret; GLXDrawable oldDraw, oldRead; @@ -604,6 +632,8 @@ PUBLIC Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext contex PUBLIC Bool glXQueryExtension(Display *dpy, int *error_base, int *event_base) { + __glXThreadInitialize(); + /* * There isn't enough information to dispatch to a vendor's * implementation, so handle the request here. @@ -624,6 +654,8 @@ PUBLIC Bool glXQueryExtension(Display *dpy, int *error_base, int *event_base) PUBLIC Bool glXQueryVersion(Display *dpy, int *major, int *minor) { + __glXThreadInitialize(); + /* * There isn't enough information to dispatch to a vendor's * implementation, so handle the request here. @@ -681,6 +713,8 @@ PUBLIC Bool glXQueryVersion(Display *dpy, int *major, int *minor) PUBLIC void glXSwapBuffers(Display *dpy, GLXDrawable drawable) { + __glXThreadInitialize(); + const int screen = __glXScreenFromDrawable(dpy, drawable); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -690,6 +724,8 @@ PUBLIC void glXSwapBuffers(Display *dpy, GLXDrawable drawable) PUBLIC void glXUseXFont(Font font, int first, int count, int list_base) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch = __glXGetCurrentDispatch(); pDispatch->glx14ep.useXFont(font, first, count, list_base); @@ -698,6 +734,8 @@ PUBLIC void glXUseXFont(Font font, int first, int count, int list_base) PUBLIC void glXWaitGL(void) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch = __glXGetCurrentDispatch(); pDispatch->glx14ep.waitGL(); @@ -706,6 +744,8 @@ PUBLIC void glXWaitGL(void) PUBLIC void glXWaitX(void) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch = __glXGetCurrentDispatch(); pDispatch->glx14ep.waitX(); @@ -716,6 +756,8 @@ PUBLIC void glXWaitX(void) PUBLIC const char *glXGetClientString(Display *dpy, int name) { + __glXThreadInitialize(); + int num_screens = XScreenCount(dpy); int screen; size_t n = CLIENT_STRING_BUFFER_SIZE - 1; @@ -757,6 +799,8 @@ PUBLIC const char *glXGetClientString(Display *dpy, int name) PUBLIC const char *glXQueryServerString(Display *dpy, int screen, int name) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); return pDispatch->glx14ep.queryServerString(dpy, screen, name); @@ -765,6 +809,8 @@ PUBLIC const char *glXQueryServerString(Display *dpy, int screen, int name) PUBLIC const char *glXQueryExtensionsString(Display *dpy, int screen) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); return pDispatch->glx14ep.queryExtensionsString(dpy, screen); @@ -773,6 +819,8 @@ PUBLIC const char *glXQueryExtensionsString(Display *dpy, int screen) PUBLIC Display *glXGetCurrentDisplay(void) { + __glXThreadInitialize(); + __GLXAPIState *apiState = __glXGetCurrentAPIState(); return apiState->currentDisplay; } @@ -781,6 +829,8 @@ PUBLIC Display *glXGetCurrentDisplay(void) PUBLIC GLXFBConfig *glXChooseFBConfig(Display *dpy, int screen, const int *attrib_list, int *nelements) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); GLXFBConfig *fbconfigs = pDispatch->glx14ep.chooseFBConfig(dpy, screen, attrib_list, nelements); @@ -800,6 +850,8 @@ PUBLIC GLXContext glXCreateNewContext(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct) { + __glXThreadInitialize(); + const int screen = __glXScreenFromFBConfig(config); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -814,6 +866,8 @@ PUBLIC GLXContext glXCreateNewContext(Display *dpy, GLXFBConfig config, PUBLIC GLXPbuffer glXCreatePbuffer(Display *dpy, GLXFBConfig config, const int *attrib_list) { + __glXThreadInitialize(); + const int screen = __glXScreenFromFBConfig(config); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -828,6 +882,8 @@ PUBLIC GLXPbuffer glXCreatePbuffer(Display *dpy, GLXFBConfig config, PUBLIC GLXPixmap glXCreatePixmap(Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list) { + __glXThreadInitialize(); + const int screen = __glXScreenFromFBConfig(config); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -843,6 +899,8 @@ PUBLIC GLXPixmap glXCreatePixmap(Display *dpy, GLXFBConfig config, PUBLIC GLXWindow glXCreateWindow(Display *dpy, GLXFBConfig config, Window win, const int *attrib_list) { + __glXThreadInitialize(); + const int screen = __glXScreenFromFBConfig(config); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -857,6 +915,8 @@ PUBLIC GLXWindow glXCreateWindow(Display *dpy, GLXFBConfig config, PUBLIC void glXDestroyPbuffer(Display *dpy, GLXPbuffer pbuf) { + __glXThreadInitialize(); + const int screen = __glXScreenFromDrawable(dpy, pbuf); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -868,6 +928,8 @@ PUBLIC void glXDestroyPbuffer(Display *dpy, GLXPbuffer pbuf) PUBLIC void glXDestroyPixmap(Display *dpy, GLXPixmap pixmap) { + __glXThreadInitialize(); + const int screen = __glXScreenFromDrawable(dpy, pixmap); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -879,6 +941,8 @@ PUBLIC void glXDestroyPixmap(Display *dpy, GLXPixmap pixmap) PUBLIC void glXDestroyWindow(Display *dpy, GLXWindow win) { + __glXThreadInitialize(); + const int screen = __glXScreenFromDrawable(dpy, win); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -890,6 +954,8 @@ PUBLIC void glXDestroyWindow(Display *dpy, GLXWindow win) PUBLIC GLXDrawable glXGetCurrentReadDrawable(void) { + __glXThreadInitialize(); + __GLXAPIState *apiState = __glXGetCurrentAPIState(); return apiState->currentRead; } @@ -898,6 +964,8 @@ PUBLIC GLXDrawable glXGetCurrentReadDrawable(void) PUBLIC int glXGetFBConfigAttrib(Display *dpy, GLXFBConfig config, int attribute, int *value) { + __glXThreadInitialize(); + const int screen = __glXScreenFromFBConfig(config); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -907,6 +975,8 @@ PUBLIC int glXGetFBConfigAttrib(Display *dpy, GLXFBConfig config, PUBLIC GLXFBConfig *glXGetFBConfigs(Display *dpy, int screen, int *nelements) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); GLXFBConfig *fbconfigs = pDispatch->glx14ep.getFBConfigs(dpy, screen, nelements); int i; @@ -924,6 +994,8 @@ PUBLIC GLXFBConfig *glXGetFBConfigs(Display *dpy, int screen, int *nelements) PUBLIC void glXGetSelectedEvent(Display *dpy, GLXDrawable draw, unsigned long *event_mask) { + __glXThreadInitialize(); + const int screen = __glXScreenFromDrawable(dpy, draw); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -933,6 +1005,8 @@ PUBLIC void glXGetSelectedEvent(Display *dpy, GLXDrawable draw, PUBLIC XVisualInfo *glXGetVisualFromFBConfig(Display *dpy, GLXFBConfig config) { + __glXThreadInitialize(); + const int screen = __glXScreenFromFBConfig(config); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -942,6 +1016,8 @@ PUBLIC XVisualInfo *glXGetVisualFromFBConfig(Display *dpy, GLXFBConfig config) PUBLIC Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext context) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch; Bool ret; GLXDrawable oldDraw, oldRead; @@ -1018,6 +1094,8 @@ PUBLIC Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, PUBLIC int glXQueryContext(Display *dpy, GLXContext context, int attribute, int *value) { + __glXThreadInitialize(); + const int screen = __glXScreenFromContext(context); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -1028,6 +1106,8 @@ PUBLIC int glXQueryContext(Display *dpy, GLXContext context, int attribute, int PUBLIC void glXQueryDrawable(Display *dpy, GLXDrawable draw, int attribute, unsigned int *value) { + __glXThreadInitialize(); + const int screen = __glXScreenFromDrawable(dpy, draw); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -1037,6 +1117,8 @@ PUBLIC void glXQueryDrawable(Display *dpy, GLXDrawable draw, PUBLIC void glXSelectEvent(Display *dpy, GLXDrawable draw, unsigned long event_mask) { + __glXThreadInitialize(); + const int screen = __glXScreenFromDrawable(dpy, draw); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -1128,6 +1210,11 @@ void cacheInitializeOnce(void) } +static void CleanupProcAddressEntry(void *unused, __GLXprocAddressHash *pEntry) +{ + free(pEntry->procName); +} + /* * This function is called externally by the libGL wrapper library to * retrieve libGLX entrypoints. @@ -1183,11 +1270,15 @@ static void cacheProcAddress(const GLubyte *procName, __GLXextFuncPtr addr) PUBLIC __GLXextFuncPtr glXGetProcAddressARB(const GLubyte *procName) { + __glXThreadInitialize(); + return glXGetProcAddress(procName); } PUBLIC __GLXextFuncPtr glXGetProcAddress(const GLubyte *procName) { + __glXThreadInitialize(); + __GLXextFuncPtr addr = NULL; /* @@ -1225,6 +1316,124 @@ done: return addr; } +int AtomicIncrement(int volatile *val) +{ + return __sync_add_and_fetch(val, 1); +} + +int AtomicSwap(int volatile *val, int newVal) +{ + return __sync_lock_test_and_set(val, newVal); +} + +int AtomicCompareAndSwap(int volatile *val, int oldVal, int newVal) +{ + return __sync_val_compare_and_swap(val, oldVal, newVal); +} + +int AtomicDecrementClampAtZero(int volatile *val) +{ + int oldVal, newVal; + + oldVal = *val; + newVal = oldVal; + + do { + if (oldVal <= 0) { + assert(oldVal == 0); + } else { + newVal = oldVal - 1; + if (newVal < 0) { + newVal = 0; + } + oldVal = AtomicCompareAndSwap(val, oldVal, newVal); + } + } while ((oldVal > 0) && (newVal != oldVal - 1)); + + return newVal; +} + +static void __glXResetOnFork(void); + +/* + * Perform checks that need to occur when entering any GLX entrypoint. + * Currently, this only detects whether a fork occurred since the last + * entrypoint was called, and performs recovery as needed. + */ +void __glXThreadInitialize(void) +{ + volatile static int g_threadsInCheck = 0; + volatile static int g_lastPid = -1; + + int lastPid; + int pid = getpid(); + + AtomicIncrement(&g_threadsInCheck); + + lastPid = AtomicSwap(&g_lastPid, pid); + + if ((lastPid != -1) && + (lastPid != pid)) { + + DBG_PRINTF(0, "Fork detected\n"); + + __glXResetOnFork(); + + // Force g_threadsInCheck to 0 to unblock other threads waiting here. + g_threadsInCheck = 0; + } else { + AtomicDecrementClampAtZero(&g_threadsInCheck); + while (g_threadsInCheck > 0) { + // Wait for other threads to finish checking for a fork. + // + // If a fork happens while g_threadsInCheck > 0 the _first_ thread + // to enter __glXThreadInitialize() will see the fork, handle it, and force + // g_threadsInCheck to 0, unblocking any other threads stuck here. + sched_yield(); + } + } +} + +static void __glXAPITeardown(Bool doReset) +{ + /* Clear the current TSD context */ + __glXPthreadFuncs.setspecific(tsdContextKey, NULL); + + /* + * XXX: This will leave dangling screen-context mappings, but they will be + * cleared separately in __glXMappingTeardown(). + */ + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXcurrentContextHash, + __glXCurrentContextHash, NULL, NULL, doReset); + + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXAPIState, + __glXAPIStateHash, CleanupAPIStateEntry, + NULL, doReset); + + if (doReset) { + /* + * XXX: We should be able to get away with just resetting the proc address + * hash lock, and not throwing away cached addresses. + */ + __glXPthreadFuncs.rwlock_init(&__glXProcAddressHash.lock, NULL); + } else { + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXprocAddressHash, + __glXProcAddressHash, CleanupProcAddressEntry, + NULL, False); + } +} + +static void __glXResetOnFork(void) +{ + /* Reset all GLX API state */ + __glXAPITeardown(True); + + /* Reset all mapping state */ + __glXMappingTeardown(True); + + /* Reset GLdispatch */ + __glDispatchReset(); +} void __attribute__ ((constructor)) __glXInit(void) { @@ -1244,6 +1453,8 @@ void __attribute__ ((constructor)) __glXInit(void) } } + /* TODO install fork handlers using __register_atfork */ + /* Register our XCloseDisplay() callback */ XGLVRegisterCloseDisplayCallback(DisplayClosed); @@ -1253,7 +1464,32 @@ void __attribute__ ((constructor)) __glXInit(void) void __attribute__ ((destructor)) __glXFini(void) { - // TODO teardown code here + /* Check for a fork before going further. */ + __glXThreadInitialize(); + + /* + * If libGLX owns the current API state, lose current + * in GLdispatch before going further. + */ + __GLdispatchAPIState *glas = + __glDispatchGetCurrentAPIState(); + + if (glas && glas->tag == GLDISPATCH_API_GLX) { + __glDispatchLoseCurrent(); + } + + + /* Unregister all XCloseDisplay() callbacks */ + XGLVUnregisterCloseDisplayCallbacks(); + + /* Tear down all GLX API state */ + __glXAPITeardown(False); + + /* Tear down all mapping state */ + __glXMappingTeardown(False); + + /* Tear down GLdispatch if necessary */ + __glDispatchFini(); } __GLXdispatchTableDynamic *__glXGetCurrentDynDispatch(void) diff --git a/src/GLX/libglxcurrent.h b/src/GLX/libglxcurrent.h index 56f0461..19b2da8 100644 --- a/src/GLX/libglxcurrent.h +++ b/src/GLX/libglxcurrent.h @@ -37,7 +37,7 @@ #include "libglxmapping.h" #include "libglxnoop.h" #include "GLdispatch.h" -#include "uthash.h" +#include "lkdhash.h" /* * Define current API library state here. An API state is per-thread, per-winsys diff --git a/src/GLX/libglxmapping.c b/src/GLX/libglxmapping.c index e6e9802..49a1db0 100644 --- a/src/GLX/libglxmapping.c +++ b/src/GLX/libglxmapping.c @@ -149,7 +149,16 @@ static GLboolean AllocDispatchIndex(__GLXvendorInfo *vendor, return GL_TRUE; } -/* +/*! + * Callback function used when freeing the dispatch index hash table. + */ +static void CleanupDispatchIndexEntry(void *unused, __GLXdispatchIndexHash *pEntry) +{ + assert(pEntry); + free(pEntry->procName); +} + +/*! * This function queries each loaded vendor to determine if there is * a vendor-implemented dispatch function. The dispatch function * uses the vendor <-> API library ABI to determine the screen given @@ -292,6 +301,26 @@ static char *ConstructVendorLibraryFilename(const char *vendorName) return filename; } +void TeardownVendor(__GLXvendorInfo *vendor, Bool doLibraryUnload) +{ + free(vendor->name); + if (vendor->glDispatch) { + __glDispatchDestroyTable(vendor->glDispatch); + } + + /* Clean up the dynamic dispatch table */ + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXdispatchFuncHash, + vendor->dynDispatch->hash, NULL, NULL, True); + + free(vendor->dynDispatch); + + if (doLibraryUnload) { + dlclose(vendor->dlhandle); + } + + free(vendor); +} + __GLXvendorInfo *__glXLookupVendorByName(const char *vendorName) { __GLXvendorNameHash *pEntry = NULL; @@ -407,19 +436,18 @@ fail: dlclose(dlhandle); } if (vendor) { - free(vendor->name); - if (vendor->glDispatch) { - __glDispatchDestroyTable(vendor->glDispatch); - } - free(vendor->dynDispatch); - } - if (pEntry) { - free(pEntry->vendor); + TeardownVendor(vendor, False/* doLibraryUnload */); } free(pEntry); return NULL; } +static void CleanupVendorNameEntry(void *unused, + __GLXvendorNameHash *pEntry) +{ + TeardownVendor(pEntry->vendor, True/* doLibraryUnload */); +} + __GLXvendorInfo *__glXLookupVendorByScreen(Display *dpy, const int screen) { __GLXvendorInfo *vendor = NULL; @@ -525,6 +553,8 @@ __GLdispatchTable *__glXGetGLDispatch(Display *dpy, const int screen) __GLXdispatchTableDynamic *__glXGetDynDispatch(Display *dpy, const int screen) { + __glXThreadInitialize(); + __GLXvendorInfo *vendor = __glXLookupVendorByScreen(dpy, screen); if (vendor) { @@ -761,3 +791,45 @@ int __glXScreenFromDrawable(Display *dpy, GLXDrawable drawable) { return ScreenFromXID(dpy, drawable); } + +/*! + * This handles freeing all mapping state during library teardown + * and fork recovery. + */ +void __glXMappingTeardown(Bool doReset) +{ + /* Tear down all hashtables used in this file */ + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXdispatchIndexHash, + __glXDispatchIndexHash, CleanupDispatchIndexEntry, + NULL, doReset); + + LKDHASH_WRLOCK(__glXPthreadFuncs, __glXDispatchIndexHash); + __glXNextUnusedHashIndex = 0; + LKDHASH_UNLOCK(__glXPthreadFuncs, __glXDispatchIndexHash); + + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXvendorScreenHash, + __glXVendorScreenHash, NULL, NULL, doReset); + + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXscreenPointerMappingHash, + __glXScreenPointerMappingHash, NULL, NULL, doReset); + + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXscreenXIDMappingHash, + __glXScreenXIDMappingHash, NULL, NULL, doReset); + + if (doReset) { + /* + * If we're just doing fork recovery, we don't actually want to unload + * any currently loaded vendors. Just reset the corresponding lock. + */ + __glXPthreadFuncs.rwlock_init(&__glXVendorNameHash.lock, NULL); + } else { + /* + * This implicitly unloads vendor libraries that were loaded when + * they were added to this hashtable. + */ + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXvendorNameHash, + __glXVendorNameHash, CleanupVendorNameEntry, + NULL, False); + } + +} diff --git a/src/GLX/libglxmapping.h b/src/GLX/libglxmapping.h index 6c27b7b..e84efc7 100644 --- a/src/GLX/libglxmapping.h +++ b/src/GLX/libglxmapping.h @@ -88,9 +88,8 @@ void __glXNotifyContextDestroyed(GLXContext ctx); /* * Close the vendor library and perform any relevant teardown. This should - * be called on each vendor when the API library is unloaded. - * TODO implement me + * be called when the API library is unloaded. */ -void __glXUnloadVendor(__GLXvendorInfo *vendor); +void __glXMappingTeardown(Bool doReset); #endif /* __LIB_GLX_MAPPING_H */ diff --git a/src/GLX/libglxthread.h b/src/GLX/libglxthread.h index 7e9fac1..9caa213 100644 --- a/src/GLX/libglxthread.h +++ b/src/GLX/libglxthread.h @@ -34,4 +34,6 @@ extern GLVNDPthreadFuncs __glXPthreadFuncs; +void __glXThreadInitialize(); + #endif -- GitLab From b7f44ea20bc17d41708b1293490e66651a5a3053 Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Wed, 29 Oct 2014 13:53:50 -0700 Subject: [PATCH 11/13] [libglx] Replace {Track,Untrack}CurrentContext() with UpdateCurrentContext() Instead of using separate functions to add and remove functions from the global hash table of current contexts, use a single function which handles: - adding the new context and removing the old context from the global hash table - initializing the tsdContextKey (if called for the first time) and updating the tsdContextKey for this thread to contain the new context This function does _not_ handle removing a screen <-> context mapping if the old context will be destroyed after it loses current; instead, it simply returns a flag, needsUnmap, which is expected to be handled by the caller. Also, update __glXAPITeardown() to clean up screen <-> context mappings as needed while entries are being removed from the current context hash. Signed-off-by: Brian Nguyen --- src/GLX/libglx.c | 164 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 116 insertions(+), 48 deletions(-) diff --git a/src/GLX/libglx.c b/src/GLX/libglx.c index 24400b7..9329623 100644 --- a/src/GLX/libglx.c +++ b/src/GLX/libglx.c @@ -62,8 +62,6 @@ static glvnd_key_t tsdContextKey; static Bool tsdContextKeyInitialized; static glvnd_once_t threadCreateTSDContextOnceControl = GLVND_ONCE_INIT; -static void UntrackCurrentContext(GLXContext ctx); - /* * Hashtable tracking current contexts for the purpose of determining whether * glXDestroyContext() should remove the context -> screen mapping immediately, @@ -78,8 +76,15 @@ typedef struct __GLXcurrentContextHashRec { static DEFINE_INITIALIZED_LKDHASH(__GLXcurrentContextHash, __glXCurrentContextHash); +static Bool UpdateCurrentContext(GLXContext newCtx, + GLXContext tsdCtx, + Bool needsUnmapNew, + Bool *needsUnmapOld); + + static void ThreadDestroyed(void *tsdCtx) { + Bool needsUnmap; /* * If a GLX context is current in this thread, remove it from the * current context hash before destroying the thread. @@ -88,9 +93,13 @@ static void ThreadDestroyed(void *tsdCtx) * to the current context. */ LKDHASH_WRLOCK(__glXPthreadFuncs, __glXCurrentContextHash); - UntrackCurrentContext(tsdCtx); + UpdateCurrentContext(NULL, tsdCtx, False, &needsUnmap); LKDHASH_UNLOCK(__glXPthreadFuncs, __glXCurrentContextHash); + if (needsUnmap) { + __glXRemoveScreenContextMapping(tsdCtx); + } + /* * Call into GLdispatch to lose current to this thread. XXX * should we check for ownership of the API state before doing @@ -337,47 +346,99 @@ void __glXNotifyContextDestroyed(GLXContext ctx) } -/* - * Adds a context to the current context list, and updates this thread's context - * TSD entry. Note: __glXCurrentContextHash must be write-locked before calling - * this function! +/*! + * Updates the current context. This function handles: + * + * - Adding the new current context newCtx to the process-global + * __glXCurrentContextHash and updating this thread's TSD entry to + * contain this context + * - Removing the old current context oldCtx from __glXCurrentContextHash + * + * \param[in] ctx The context to make current + * \param[in] tsdCtx If this function is called from the tsdContextKey + * destructor, this should point to the old context to use. Otherwise, this + * should be set to NULL. + * \param[in] needsUnmapNew True when the new context's screen mapping + * should be removed when it is no longer current (this can happen if the make + * current operation failed and we are restoring a context which will be + * destroyed when it loses current) or False otherwise. + * \param[out] needsUnmapOld If non-NULL, points to a boolean which is set to + * indicate whether the old context's screen mapping needs to be removed + * (because the old context is about to be destroyed). + * + * Returns True on success, False otherwise. + * + * Note: __glXCurrentContextHash must be write-locked before calling this + * function! * * Returns True on success. */ -static Bool TrackCurrentContext(GLXContext ctx) -{ - __GLXcurrentContextHash *pEntry = NULL; - GLXContext tsdCtx; +static Bool UpdateCurrentContext(GLXContext newCtx, + GLXContext tsdCtx, + Bool needsUnmapNew, + Bool *needsUnmapOld) +{ + __GLXcurrentContextHash *pOldEntry, + *pTmpEntry, + *pNewEntry; + GLXContext oldCtx; + + if (needsUnmapOld) { + *needsUnmapOld = False; + } + + // Attempt the allocation first, so we can bail out early + // on failure. + if (newCtx) { + pNewEntry = malloc(sizeof(*pNewEntry)); + if (!pNewEntry) { + return False; + } + pNewEntry->ctx = newCtx; + pNewEntry->needsUnmap = needsUnmapNew; + } else { + pNewEntry = NULL; + } // Initialize the TSD entry (if we haven't already) __glXPthreadFuncs.once(&threadCreateTSDContextOnceControl, ThreadCreateTSDContextOnce); - assert(tsdContextKeyInitialized); - - // Update the TSD entry to reflect the correct current context - tsdCtx = (GLXContext)__glXPthreadFuncs.getspecific(tsdContextKey); - __glXPthreadFuncs.setspecific(tsdContextKey, ctx); + if (!tsdContextKeyInitialized) { + return False; + } - if (!ctx) { - // Don't track NULL contexts - return True; + // Update the TSD entry to reflect the correct current context. It's + // possible that this may be called from tsdContextKey's destructor. In + // that case, the NULL value has already been associated with the key and + // pthread_getspecific() will return the wrong value, so use the value + // passed in as tsdCtx instead. In the case where this is called from a + // destructor and tsdCtx == NULL, oldCtx will (correctly) be set to NULL. + if (tsdCtx) { + oldCtx = tsdCtx; + } else { + oldCtx = (GLXContext)__glXPthreadFuncs.getspecific(tsdContextKey); } + __glXPthreadFuncs.setspecific(tsdContextKey, newCtx); - HASH_FIND(hh, _LH(__glXCurrentContextHash), &ctx, sizeof(ctx), pEntry); + if (oldCtx) { + // Remove the old context from the hash table, if not NULL + HASH_FIND(hh, _LH(__glXCurrentContextHash), &oldCtx, sizeof(oldCtx), pOldEntry); - assert(!pEntry); + assert(pOldEntry); - pEntry = malloc(sizeof(*pEntry)); - if (!pEntry) { - // Restore the original TSD entry - __glXPthreadFuncs.setspecific(tsdContextKey, tsdCtx); - return False; + if (needsUnmapOld) { + *needsUnmapOld = pOldEntry->needsUnmap; + } + HASH_DELETE(hh, _LH(__glXCurrentContextHash), pOldEntry); + free(pOldEntry); } - pEntry->ctx = ctx; - pEntry->needsUnmap = False; - HASH_ADD(hh, _LH(__glXCurrentContextHash), ctx, sizeof(ctx), pEntry); + if (pNewEntry) { + HASH_FIND(hh, _LH(__glXCurrentContextHash), &newCtx, sizeof(newCtx), pTmpEntry); + assert(!pTmpEntry); + HASH_ADD(hh, _LH(__glXCurrentContextHash), ctx, sizeof(newCtx), pNewEntry); + } return True; } @@ -558,9 +619,10 @@ PUBLIC Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext contex __glXThreadInitialize(); const __GLXdispatchTableStatic *pDispatch; - Bool ret; + Bool tmpRet, ret; GLXDrawable oldDraw, oldRead; GLXContext oldContext; + Bool oldContextNeedsUnmap; SaveCurrentValues(&oldDraw, &oldRead, &oldContext); @@ -573,7 +635,7 @@ PUBLIC Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext contex } if (oldContext != context) { - if (!TrackCurrentContext(context)) { + if (!UpdateCurrentContext(context, NULL, False, &oldContextNeedsUnmap)) { /* * Fail here. Continuing on would mess up our accounting. */ @@ -612,15 +674,13 @@ PUBLIC Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext contex if (oldContext != context) { /* - * Only untrack the old context if the make current operation succeeded. - * Otherwise, untrack the new context. + * If the make current operation failed, restore the original context. */ - if (ret) { - UntrackCurrentContext(oldContext); - } else { - UntrackCurrentContext(context); - ret = TrackCurrentContext(oldContext); - assert(ret); + if (!ret) { + tmpRet = UpdateCurrentContext(oldContext, NULL, oldContextNeedsUnmap, NULL); + assert(tmpRet); + } else if (oldContextNeedsUnmap) { + __glXRemoveScreenContextMapping(oldContext); } } @@ -1019,9 +1079,10 @@ PUBLIC Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, __glXThreadInitialize(); const __GLXdispatchTableStatic *pDispatch; - Bool ret; + Bool tmpRet, ret; GLXDrawable oldDraw, oldRead; GLXContext oldContext; + Bool oldContextNeedsUnmap; SaveCurrentValues(&oldDraw, &oldRead, &oldContext); @@ -1034,7 +1095,7 @@ PUBLIC Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, } if (oldContext != context) { - if (!TrackCurrentContext(context)) { + if (!UpdateCurrentContext(context, NULL, False, &oldContextNeedsUnmap)) { /* * Fail here. Continuing on would mess up our accounting. */ @@ -1076,13 +1137,13 @@ PUBLIC Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, if (oldContext != context) { /* - * Only untrack the old context if the make current operation succeeded. - * Otherwise, untrack the new context. + * If the make current operation failed, restore the original context. */ - if (ret) { - UntrackCurrentContext(oldContext); - } else { - UntrackCurrentContext(context); + if (!ret) { + tmpRet = UpdateCurrentContext(oldContext, NULL, oldContextNeedsUnmap, NULL); + assert(tmpRet); + } else if (oldContextNeedsUnmap) { + __glXRemoveScreenContextMapping(oldContext); } } @@ -1394,6 +1455,13 @@ void __glXThreadInitialize(void) } } +void CurrentContextHashCleanup(void *unused, __GLXcurrentContextHash *pEntry) +{ + if (pEntry->needsUnmap) { + __glXRemoveScreenContextMapping(pEntry->ctx); + } +} + static void __glXAPITeardown(Bool doReset) { /* Clear the current TSD context */ @@ -1404,7 +1472,7 @@ static void __glXAPITeardown(Bool doReset) * cleared separately in __glXMappingTeardown(). */ LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXcurrentContextHash, - __glXCurrentContextHash, NULL, NULL, doReset); + __glXCurrentContextHash, CurrentContextHashCleanup, NULL, doReset); LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXAPIState, __glXAPIStateHash, CleanupAPIStateEntry, -- GitLab From cb9afbace95881a386756c56753cd88703608e45 Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Wed, 29 Oct 2014 14:08:38 -0700 Subject: [PATCH 12/13] [libglx] Update ABI to notify vendors of X errors, fix spec compliance issue This commit adds a new vendor callback, notifyError(), for notifying a vendor library when an X error should be generated. This allows the vendor to perform any needed error recovery and to generate the error. In MakeContextCurrentInternal(), check for: (context && (draw == None || read == None)) || (!context && (draw != None || read != None)) and call notifyError() on an appropriate vendor to generate BadMatch before returning False. Signed-off-by: Brian Nguyen --- src/GLX/libglx.c | 121 ++++++++++++++++++++++++++++++-------------- src/GLX/libglxabi.h | 7 +++ 2 files changed, 89 insertions(+), 39 deletions(-) diff --git a/src/GLX/libglx.c b/src/GLX/libglx.c index 9329623..0809711 100644 --- a/src/GLX/libglx.c +++ b/src/GLX/libglx.c @@ -457,32 +457,57 @@ static Bool IsContextCurrentToAnyOtherThread(GLXContext ctx) return !!pEntry && (current != ctx); } -/* - * Removes a context from the current context list, and removes any context -> - * screen mappings if necessary. - * - * Note: The __glXCurrentContextHash must be write-locked before calling this - * function! +/*! + * Given the Make{Context,}Current arguments passed in, tries to find the + * screen of an appropriate vendor to notify when an X error occurs. It's + * possible none of the arguments in this list will produce a valid screen + * number, so this will fall back to screen 0 if all else fails. */ -static void UntrackCurrentContext(GLXContext ctx) +int FindAnyValidScreenFromMakeCurrent(Display *dpy, + GLXDrawable draw, + GLXDrawable read, + GLXContext context) { - Bool needsUnmap = False; - __GLXcurrentContextHash *pEntry = NULL; - if (!ctx) { - // Don't untrack NULL contexts - return; + int screen; + + screen = __glXScreenFromContext(__glXGetCurrentContext()); + + if (screen < 0) { + screen = __glXScreenFromContext(context); } - HASH_FIND(hh, _LH(__glXCurrentContextHash), &ctx, sizeof(ctx), pEntry); + if (screen < 0) { + screen = __glXScreenFromDrawable(dpy, draw); + } - assert(pEntry); + if (screen < 0) { + screen = __glXScreenFromDrawable(dpy, read); + } - needsUnmap = pEntry->needsUnmap; - HASH_DELETE(hh, _LH(__glXCurrentContextHash), pEntry); - free(pEntry); + if (screen < 0) { + /* If no screens were found, fall back to 0 */ + screen = 0; + } - if (needsUnmap) { - __glXRemoveScreenContextMapping(ctx); + return screen; +} + +void NotifyVendorOfXError(int screen, + Display *dpy, + char errorOpcode, + char minorOpcode, + XID resid) +{ + /* + * XXX: For now, libglvnd doesn't handle generating X errors directly. + * Instead, it tries to pass the error off to an available vendor library + * so the vendor can handle generating the X error. + */ + const __GLXdispatchTableStatic *pDispatch = + __glXGetStaticDispatch(dpy, screen); + + if (pDispatch->glxvc.notifyError) { + pDispatch->glxvc.notifyError(dpy, errorOpcode, minorOpcode, resid); } } @@ -490,6 +515,7 @@ static Bool MakeContextCurrentInternal(Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext context, + char callerOpcode, const __GLXdispatchTableStatic **ppDispatch) { __GLXAPIState *apiState; @@ -500,6 +526,9 @@ static Bool MakeContextCurrentInternal(Display *dpy, DBG_PRINTF(0, "dpy = %p, draw = %x, read = %x, context = %p\n", dpy, (unsigned)draw, (unsigned)read, context); + assert(callerOpcode == X_GLXMakeCurrent || + callerOpcode == X_GLXMakeContextCurrent); + apiState = __glXGetCurrentAPIState(); oldVendor = apiState->currentVendor; screen = __glXScreenFromContext(context); @@ -515,6 +544,20 @@ static Bool MakeContextCurrentInternal(Display *dpy, return False; } + if ((!context && (draw != None || read != None)) || + (context && (draw == None || read == None))) { + int errorScreen = + FindAnyValidScreenFromMakeCurrent(dpy, draw, read, context); + /* + * If is NULL and and are not None, or + * if or are set to None and is not NULL, + * then a BadMatch error will be generated. GLX 1.4 section 3.3.7 + * (p. 27). + */ + NotifyVendorOfXError(errorScreen, dpy, BadMatch, callerOpcode, 0); + return False; + } + /* * If we have a valid screen number, there must be a valid vendor associated * with that screen. @@ -542,10 +585,6 @@ static Bool MakeContextCurrentInternal(Display *dpy, *ppDispatch = newVendor ? newVendor->staticDispatch : NULL; if (!context) { - if (draw != None || read != None) { - return False; - } - /* * Call into GLdispatch to lose current and update the context and GL * dispatch table @@ -648,6 +687,7 @@ PUBLIC Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext contex drawable, drawable, context, + X_GLXMakeCurrent, &pDispatch); if (ret) { assert(!context || pDispatch); @@ -655,18 +695,19 @@ PUBLIC Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext contex ret = pDispatch->glx14ep.makeCurrent(dpy, drawable, context); if (!ret) { // Restore the original current values - ret = MakeContextCurrentInternal(dpy, - oldDraw, - oldRead, - oldContext, - &pDispatch); - assert(ret); + tmpRet = MakeContextCurrentInternal(dpy, + oldDraw, + oldRead, + oldContext, + X_GLXMakeCurrent, + &pDispatch); + assert(tmpRet); if (pDispatch) { - ret = pDispatch->glx14ep.makeContextCurrent(dpy, + tmpRet = pDispatch->glx14ep.makeContextCurrent(dpy, oldDraw, oldRead, oldContext); - assert(ret); + assert(tmpRet); } } } @@ -1108,6 +1149,7 @@ PUBLIC Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, draw, read, context, + X_GLXMakeContextCurrent, &pDispatch); if (ret) { assert(!context || pDispatch); @@ -1118,18 +1160,19 @@ PUBLIC Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, context); if (!ret) { // Restore the original current values - ret = MakeContextCurrentInternal(dpy, - oldDraw, - oldRead, - oldContext, - &pDispatch); - assert(ret); + tmpRet = MakeContextCurrentInternal(dpy, + oldDraw, + oldRead, + oldContext, + X_GLXMakeContextCurrent, + &pDispatch); + assert(tmpRet); if (pDispatch) { - ret = pDispatch->glx14ep.makeContextCurrent(dpy, + tmpRet = pDispatch->glx14ep.makeContextCurrent(dpy, oldDraw, oldRead, oldContext); - assert(ret); + assert(tmpRet); } } } diff --git a/src/GLX/libglxabi.h b/src/GLX/libglxabi.h index 340d12f..1894e5c 100644 --- a/src/GLX/libglxabi.h +++ b/src/GLX/libglxabi.h @@ -294,6 +294,13 @@ struct __GLXvendorCallbacksRec { */ void (*setDispatchIndex) (const GLubyte *procName, int index); + /*! + * This notifies the vendor library when an X error should be generated + * due to a detected error in the GLX API stream. + */ + void (*notifyError) (Display *dpy, char error, + char opcode, XID resid); + /*! * (OPTIONAL) Callbacks by which the vendor library may re-write libglvnd's * entrypoints at make current time, provided no other contexts are current -- GitLab From 39656dd52e4f3079a9e03d092f94e7536a3354f4 Mon Sep 17 00:00:00 2001 From: Brian Nguyen Date: Wed, 29 Oct 2014 14:20:13 -0700 Subject: [PATCH 13/13] [libglxmapping] Don't clear out mapping hashtables on a fork Mappings defined in the parent should remain valid in the child process, so don't clear out mappings after a fork since they may be needed for GLX calls made in the child. Signed-off-by: Brian Nguyen --- src/GLX/libglxmapping.c | 45 ++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/GLX/libglxmapping.c b/src/GLX/libglxmapping.c index 49a1db0..40c29e0 100644 --- a/src/GLX/libglxmapping.c +++ b/src/GLX/libglxmapping.c @@ -794,35 +794,42 @@ int __glXScreenFromDrawable(Display *dpy, GLXDrawable drawable) /*! * This handles freeing all mapping state during library teardown - * and fork recovery. + * or resetting locks on fork recovery. */ void __glXMappingTeardown(Bool doReset) { - /* Tear down all hashtables used in this file */ - LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXdispatchIndexHash, - __glXDispatchIndexHash, CleanupDispatchIndexEntry, - NULL, doReset); - - LKDHASH_WRLOCK(__glXPthreadFuncs, __glXDispatchIndexHash); - __glXNextUnusedHashIndex = 0; - LKDHASH_UNLOCK(__glXPthreadFuncs, __glXDispatchIndexHash); - - LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXvendorScreenHash, - __glXVendorScreenHash, NULL, NULL, doReset); - - LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXscreenPointerMappingHash, - __glXScreenPointerMappingHash, NULL, NULL, doReset); - - LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXscreenXIDMappingHash, - __glXScreenXIDMappingHash, NULL, NULL, doReset); if (doReset) { /* * If we're just doing fork recovery, we don't actually want to unload - * any currently loaded vendors. Just reset the corresponding lock. + * any currently loaded vendors _or_ remove any mappings (they should + * still be valid in the new process, and may be needed if the child + * tries using pointers/XIDs that were created in the parent). Just + * reset the corresponding locks. */ + __glXPthreadFuncs.rwlock_init(&__glXDispatchIndexHash.lock, NULL); + __glXPthreadFuncs.rwlock_init(&__glXVendorScreenHash.lock, NULL); + __glXPthreadFuncs.rwlock_init(&__glXScreenPointerMappingHash.lock, NULL); + __glXPthreadFuncs.rwlock_init(&__glXScreenXIDMappingHash.lock, NULL); __glXPthreadFuncs.rwlock_init(&__glXVendorNameHash.lock, NULL); } else { + /* Tear down all hashtables used in this file */ + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXdispatchIndexHash, + __glXDispatchIndexHash, CleanupDispatchIndexEntry, + NULL, False); + + LKDHASH_WRLOCK(__glXPthreadFuncs, __glXDispatchIndexHash); + __glXNextUnusedHashIndex = 0; + LKDHASH_UNLOCK(__glXPthreadFuncs, __glXDispatchIndexHash); + + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXvendorScreenHash, + __glXVendorScreenHash, NULL, NULL, False); + + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXscreenPointerMappingHash, + __glXScreenPointerMappingHash, NULL, NULL, False); + + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXscreenXIDMappingHash, + __glXScreenXIDMappingHash, NULL, NULL, False); /* * This implicitly unloads vendor libraries that were loaded when * they were added to this hashtable. -- GitLab