diff --git a/include/lkdhash.h b/include/lkdhash.h index e8349c551fe16d877029ecf6dc4b1a327c08c845..355751595f207985d71f128cff6338a79f2bae0c 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 diff --git a/src/GLX/libglx.c b/src/GLX/libglx.c index a51740310340deb76b0c8049920df962eba43e51..08097117ccd4083eb8447df94a2e86ad3a32b634 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" @@ -60,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, @@ -76,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. @@ -86,8 +93,19 @@ 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 + * this? + */ + __glDispatchLoseCurrent(); } static void ThreadCreateTSDContextOnce(void) @@ -100,6 +118,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); @@ -126,6 +146,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); @@ -139,6 +161,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); @@ -150,6 +174,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); @@ -163,6 +189,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); @@ -174,6 +202,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) { @@ -187,12 +217,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; } @@ -200,6 +234,8 @@ PUBLIC GLXDrawable glXGetCurrentDrawable(void) PUBLIC Bool glXIsDirect(Display *dpy, GLXContext context) { + __glXThreadInitialize(); + const int screen = __glXScreenFromContext(context); const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -242,6 +278,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) { @@ -304,46 +346,99 @@ void __glXNotifyContextDestroyed(GLXContext ctx) } -/* - * Adds a context to the current context list. 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) +static Bool UpdateCurrentContext(GLXContext newCtx, + GLXContext tsdCtx, + Bool needsUnmapNew, + Bool *needsUnmapOld) { - __GLXcurrentContextHash *pEntry = NULL; - GLXContext tsdCtx; + __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; } @@ -362,36 +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. TODO: need to handle the corner case where the - * thread is terminated and we haven't lost current to this context - * - * 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; + } - // Clear the TSD entry - __glXPthreadFuncs.setspecific(tsdContextKey, NULL); + return screen; +} - if (needsUnmap) { - __glXRemoveScreenContextMapping(ctx); +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); } } @@ -399,6 +515,7 @@ static Bool MakeContextCurrentInternal(Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext context, + char callerOpcode, const __GLXdispatchTableStatic **ppDispatch) { __GLXAPIState *apiState; @@ -409,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); @@ -424,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. @@ -451,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 @@ -525,10 +655,13 @@ static void SaveCurrentValues(GLXDrawable *pDraw, PUBLIC Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext context) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch; - Bool ret; + Bool tmpRet, ret; GLXDrawable oldDraw, oldRead; GLXContext oldContext; + Bool oldContextNeedsUnmap; SaveCurrentValues(&oldDraw, &oldRead, &oldContext); @@ -541,7 +674,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. */ @@ -554,6 +687,7 @@ PUBLIC Bool glXMakeCurrent(Display *dpy, GLXDrawable drawable, GLXContext contex drawable, drawable, context, + X_GLXMakeCurrent, &pDispatch); if (ret) { assert(!context || pDispatch); @@ -561,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); } } } @@ -580,13 +715,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); + if (!ret) { + tmpRet = UpdateCurrentContext(oldContext, NULL, oldContextNeedsUnmap, NULL); + assert(tmpRet); + } else if (oldContextNeedsUnmap) { + __glXRemoveScreenContextMapping(oldContext); } } @@ -598,6 +733,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. @@ -618,6 +755,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. @@ -675,6 +814,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); @@ -684,6 +825,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); @@ -692,6 +835,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(); @@ -700,6 +845,8 @@ PUBLIC void glXWaitGL(void) PUBLIC void glXWaitX(void) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch = __glXGetCurrentDispatch(); pDispatch->glx14ep.waitX(); @@ -710,6 +857,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; @@ -751,6 +900,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); @@ -759,6 +910,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); @@ -767,6 +920,8 @@ PUBLIC const char *glXQueryExtensionsString(Display *dpy, int screen) PUBLIC Display *glXGetCurrentDisplay(void) { + __glXThreadInitialize(); + __GLXAPIState *apiState = __glXGetCurrentAPIState(); return apiState->currentDisplay; } @@ -775,6 +930,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); @@ -794,6 +951,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); @@ -808,6 +967,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); @@ -822,6 +983,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); @@ -837,6 +1000,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); @@ -851,6 +1016,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); @@ -862,6 +1029,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); @@ -873,6 +1042,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); @@ -884,6 +1055,8 @@ PUBLIC void glXDestroyWindow(Display *dpy, GLXWindow win) PUBLIC GLXDrawable glXGetCurrentReadDrawable(void) { + __glXThreadInitialize(); + __GLXAPIState *apiState = __glXGetCurrentAPIState(); return apiState->currentRead; } @@ -892,6 +1065,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); @@ -901,6 +1076,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; @@ -918,6 +1095,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); @@ -927,6 +1106,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); @@ -936,10 +1117,13 @@ PUBLIC XVisualInfo *glXGetVisualFromFBConfig(Display *dpy, GLXFBConfig config) PUBLIC Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext context) { + __glXThreadInitialize(); + const __GLXdispatchTableStatic *pDispatch; - Bool ret; + Bool tmpRet, ret; GLXDrawable oldDraw, oldRead; GLXContext oldContext; + Bool oldContextNeedsUnmap; SaveCurrentValues(&oldDraw, &oldRead, &oldContext); @@ -952,7 +1136,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. */ @@ -965,6 +1149,7 @@ PUBLIC Bool glXMakeContextCurrent(Display *dpy, GLXDrawable draw, draw, read, context, + X_GLXMakeContextCurrent, &pDispatch); if (ret) { assert(!context || pDispatch); @@ -975,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); } } } @@ -994,13 +1180,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); } } @@ -1012,6 +1198,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); @@ -1022,6 +1210,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); @@ -1031,6 +1221,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); @@ -1122,6 +1314,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. @@ -1177,11 +1374,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; /* @@ -1219,6 +1420,131 @@ 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(); + } + } +} + +void CurrentContextHashCleanup(void *unused, __GLXcurrentContextHash *pEntry) +{ + if (pEntry->needsUnmap) { + __glXRemoveScreenContextMapping(pEntry->ctx); + } +} + +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, CurrentContextHashCleanup, 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) { @@ -1238,6 +1564,8 @@ void __attribute__ ((constructor)) __glXInit(void) } } + /* TODO install fork handlers using __register_atfork */ + /* Register our XCloseDisplay() callback */ XGLVRegisterCloseDisplayCallback(DisplayClosed); @@ -1247,7 +1575,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/libglxabi.h b/src/GLX/libglxabi.h index 340d12fcfad49305239d191f0f2b2bffbb412b28..1894e5c4add2c9a9ffeefaec16e2438bae0c3490 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 diff --git a/src/GLX/libglxcurrent.h b/src/GLX/libglxcurrent.h index 56f0461bea59918cd8ab0f48606128566fb79a8e..19b2da8be487a2486723083957832785609d34cb 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 f6a4ce8fe119a6db362371d8e6e474760f08ebb3..40c29e05e989bc7db182b270399ff6f712e18467 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,10 +301,29 @@ 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; - char *filename; void *dlhandle = NULL; __PFNGLXMAINPROC glxMainProc; const __GLXdispatchTableStatic *dispatch; @@ -318,6 +346,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 +356,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; @@ -404,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; @@ -522,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) { @@ -758,3 +791,52 @@ int __glXScreenFromDrawable(Display *dpy, GLXDrawable drawable) { return ScreenFromXID(dpy, drawable); } + +/*! + * This handles freeing all mapping state during library teardown + * or resetting locks on fork recovery. + */ +void __glXMappingTeardown(Bool doReset) +{ + + if (doReset) { + /* + * If we're just doing fork recovery, we don't actually want to unload + * 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. + */ + LKDHASH_TEARDOWN(__glXPthreadFuncs, __GLXvendorNameHash, + __glXVendorNameHash, CleanupVendorNameEntry, + NULL, False); + } + +} diff --git a/src/GLX/libglxmapping.h b/src/GLX/libglxmapping.h index 6c27b7bbe7f8aa53d095f6751ae1c4fc1134d6d6..e84efc7be9302273a6351d52a4ad9d9446cfb5e7 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 7e9fac1834e466ecbe489ac91bea17269ccf4050..9caa2138db6179e9b89ddb0743a79de7cc9849ef 100644 --- a/src/GLX/libglxthread.h +++ b/src/GLX/libglxthread.h @@ -34,4 +34,6 @@ extern GLVNDPthreadFuncs __glXPthreadFuncs; +void __glXThreadInitialize(); + #endif diff --git a/src/GLdispatch/GLdispatch.c b/src/GLdispatch/GLdispatch.c index eed5d6d7f56db7f93e03cf4e95214f82491f7d42..d046ac059723651644c41a324542262828485a5c 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; /* @@ -118,13 +126,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. @@ -164,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? @@ -183,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. @@ -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 1ec9984549dff2eea3c1cc311f1cee45f2fb608f..a88ed7e019c03f7ce9b3b75a51cfbbc41b3df34f 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. diff --git a/src/util/glvnd_pthread/glvnd_pthread.c b/src/util/glvnd_pthread/glvnd_pthread.c index e72db8695377258a6ca258e3158640d323911753..2c66ce40c4d551353712bd15f92521b7219e7856 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 1c984ee7f48c15c13b459cc3903ad77d780daff9..259f3ac4cb6bea0b530e1f95c87605a825708e16 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); diff --git a/src/x11glvnd/x11glvnd.h b/src/x11glvnd/x11glvnd.h index 165d276a689236623c05827048689785ec56c53b..93428846c3e8256b654c2111ab73b59d40933cb4 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 9ae0134f3da1d3357dcc26b874392cf87ae037dd..a10163646dac7430d5da62fa7d0641565ad0858e 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)