diff options
author | Brian Nguyen <brnguyen@nvidia.com> | 2013-11-21 14:02:20 -0800 |
---|---|---|
committer | brnguyen <brnguyen@nvidia.com> | 2013-11-26 18:38:04 -0800 |
commit | 7a7d49bf0c8263c5f25be96a7fda2ffe78942112 (patch) | |
tree | 0165adea65c36f4ab0989b0fd7f7da75d8c5b0b9 /src | |
parent | 92dfd9c5fe8f5f169c6bba7cb87b2389bb8780c6 (diff) |
Use a TSD key destructor to untrack current contexts of terminated threads
If a thread is destroyed with a context current, libglvnd needs to be
able to remove the context from the current context list to prevent
failures if another thread attempts to make current to the context.
Define a TSD key which stores a pointer that mirrors the current context
pointer stored in TLS, and update this key in TrackCurrentContext().
Define a destructor for this key which calls UntrackCurrentContext()
on this pointer when the thread is terminated.
Signed-off-by: Brian Nguyen <brnguyen@nvidia.com>
Reviewed-by: Aaron Plattner <aplattner@nvidia.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/GLX/libglx.c | 73 | ||||
-rw-r--r-- | src/GLX/libglxmapping.c | 6 | ||||
-rw-r--r-- | src/GLX/libglxthread.h | 2 | ||||
-rw-r--r-- | src/util/glvnd_pthread/glvnd_pthread.c | 57 | ||||
-rw-r--r-- | src/util/glvnd_pthread/glvnd_pthread.h | 12 |
5 files changed, 136 insertions, 14 deletions
diff --git a/src/GLX/libglx.c b/src/GLX/libglx.c index 787e5db..8cf225f 100644 --- a/src/GLX/libglx.c +++ b/src/GLX/libglx.c @@ -52,6 +52,55 @@ GLVNDPthreadFuncs __glXPthreadFuncs; +static glvnd_key_t threadDestroyKey; +static Bool threadDestroyKeyInitialized; +static glvnd_once_t threadDestroyKeyCreateOnceControl = 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, + * or defer this until the context loses current. + */ +typedef struct __GLXcurrentContextHashRec { + GLXContext ctx; + Bool needsUnmap; + UT_hash_handle hh; +} __GLXcurrentContextHash; + +static DEFINE_INITIALIZED_LKDHASH(__GLXcurrentContextHash, + __glXCurrentContextHash); + + + +static void ThreadDestroyed(void *tsdCtx) +{ + /* + * If a GLX context is current in this thread, remove it from the + * current context hash before destroying the thread. + * + * The TSD key associated with this destructor contains a pointer + * to the current context. + */ + LKDHASH_WRLOCK(__glXPthreadFuncs, __glXCurrentContextHash); + UntrackCurrentContext(tsdCtx); + LKDHASH_UNLOCK(__glXPthreadFuncs, __glXCurrentContextHash); +} + +static void ThreadDestroyKeyCreateOnce(void) +{ + int ret = __glXPthreadFuncs.key_create(&threadDestroyKey, ThreadDestroyed); + assert(!ret); + + threadDestroyKeyInitialized = True; +} + +void __glXInitThreads(void) +{ + __glXPthreadFuncs.once(&threadDestroyKeyCreateOnceControl, ThreadDestroyKeyCreateOnce); +} + PUBLIC XVisualInfo* glXChooseVisual(Display *dpy, int screen, int *attrib_list) { const __GLXdispatchTableStatic *pDispatch = __glXGetStaticDispatch(dpy, screen); @@ -212,20 +261,6 @@ __GLXAPIState *__glXGetAPIState(glvnd_thread_t tid) } /* - * Hashtable tracking current contexts for the purpose of determining whether - * glXDestroyContext() should remove the context -> screen mapping immediately, - * or defer this until the context loses current. - */ -typedef struct __GLXcurrentContextHashRec { - GLXContext ctx; - Bool needsUnmap; - UT_hash_handle hh; -} __GLXcurrentContextHash; - -static DEFINE_INITIALIZED_LKDHASH(__GLXcurrentContextHash, - __glXCurrentContextHash); - -/* * Notifies libglvnd that the given context has been marked for destruction * by glXDestroyContext(), and removes any context -> screen mappings if * necessary. @@ -268,6 +303,14 @@ void __glXNotifyContextDestroyed(GLXContext ctx) static Bool TrackCurrentContext(GLXContext ctx) { __GLXcurrentContextHash *pEntry = NULL; + GLXContext tsdCtx; + + assert(threadDestroyKeyInitialized); + + // Update the TSD entry to reflect the correct current context + tsdCtx = (GLXContext)__glXPthreadFuncs.getspecific(threadDestroyKey); + __glXPthreadFuncs.setspecific(threadDestroyKey, ctx); + if (!ctx) { // Don't track NULL contexts return True; @@ -279,6 +322,8 @@ static Bool TrackCurrentContext(GLXContext ctx) pEntry = malloc(sizeof(*pEntry)); if (!pEntry) { + // Restore the original TSD entry + __glXPthreadFuncs.setspecific(threadDestroyKey, tsdCtx); return False; } diff --git a/src/GLX/libglxmapping.c b/src/GLX/libglxmapping.c index 9f1d7ea..89500aa 100644 --- a/src/GLX/libglxmapping.c +++ b/src/GLX/libglxmapping.c @@ -400,6 +400,12 @@ __GLXvendorInfo *__glXLookupVendorByScreen(Display *dpy, const int screen) return NULL; } + /* + * This is the first thing done by most vendor-neutral GLX entrypoints, and + * hence a good time to do any necessary per-thread initialization. + */ + __glXInitThreads(); + memset(&key, 0, sizeof(key)); key.dpy = dpy; diff --git a/src/GLX/libglxthread.h b/src/GLX/libglxthread.h index 7e9fac1..bc7c061 100644 --- a/src/GLX/libglxthread.h +++ b/src/GLX/libglxthread.h @@ -34,4 +34,6 @@ extern GLVNDPthreadFuncs __glXPthreadFuncs; +void __glXInitThreads(void); + #endif diff --git a/src/util/glvnd_pthread/glvnd_pthread.c b/src/util/glvnd_pthread/glvnd_pthread.c index 508ecc9..efe15e9 100644 --- a/src/util/glvnd_pthread/glvnd_pthread.c +++ b/src/util/glvnd_pthread/glvnd_pthread.c @@ -56,6 +56,15 @@ typedef struct GLVNDPthreadRealFuncsRec { /* Other used functions */ int (*once)(pthread_once_t *once_control, void (*init_routine)(void)); + + /* + * TSD key management. Used to handle the corner case when a thread + * is destroyed with a context current. + */ + int (*key_create)(pthread_key_t *key, void (*destr_function)(void *)); + int (*key_delete)(pthread_key_t key); + int (*setspecific)(pthread_key_t key, const void *p); + void *(*getspecific)(pthread_key_t key); } GLVNDPthreadRealFuncs; static GLVNDPthreadRealFuncs pthreadRealFuncs; @@ -153,6 +162,26 @@ static int st_once(glvnd_once_t *once_control, void (*init_routine)(void)) return 0; } +static int st_key_create(glvnd_key_t *key, void (*destr_function)(void *)) +{ + return 0; +} + +static int st_key_delete(glvnd_key_t key) +{ + return 0; +} + +static int st_setspecific(glvnd_key_t key, const void *p) +{ + return 0; +} + +static void *st_getspecific(glvnd_key_t key) +{ + return NULL; +} + /* Multi-threaded functions */ static int mt_create(glvnd_thread_t *thread, const glvnd_thread_attr_t *attr, @@ -222,6 +251,26 @@ static int mt_once(glvnd_once_t *once_control, void (*init_routine)(void)) return pthreadRealFuncs.once(&once_control->once, init_routine); } +static int mt_key_create(glvnd_key_t *key, void (*destr_function)(void *)) +{ + return pthreadRealFuncs.key_create(key, destr_function); +} + +static int mt_key_delete(glvnd_key_t key) +{ + return pthreadRealFuncs.key_delete(key); +} + +static int mt_setspecific(glvnd_key_t key, const void *p) +{ + return pthreadRealFuncs.setspecific(key, p); +} + +static void *mt_getspecific(glvnd_key_t key) +{ + return pthreadRealFuncs.getspecific(key); +} + int glvndSetupPthreads(void *dlhandle, GLVNDPthreadFuncs *funcs) { char *force_st = getenv("__GL_SINGLETHREADED"); @@ -244,6 +293,10 @@ int glvndSetupPthreads(void *dlhandle, GLVNDPthreadFuncs *funcs) GET_MT_FUNC(funcs, dlhandle, rwlock_wrlock); GET_MT_FUNC(funcs, dlhandle, rwlock_unlock); GET_MT_FUNC(funcs, dlhandle, once); + GET_MT_FUNC(funcs, dlhandle, key_create); + GET_MT_FUNC(funcs, dlhandle, key_delete); + GET_MT_FUNC(funcs, dlhandle, setspecific); + GET_MT_FUNC(funcs, dlhandle, getspecific); // Multi-threaded return 1; @@ -265,6 +318,10 @@ fail: GET_ST_FUNC(funcs, rwlock_wrlock); GET_ST_FUNC(funcs, rwlock_unlock); GET_ST_FUNC(funcs, once); + GET_ST_FUNC(funcs, key_create); + GET_ST_FUNC(funcs, key_delete); + GET_ST_FUNC(funcs, setspecific); + GET_ST_FUNC(funcs, getspecific); // Single-threaded diff --git a/src/util/glvnd_pthread/glvnd_pthread.h b/src/util/glvnd_pthread/glvnd_pthread.h index 8da1db9..bf22f23 100644 --- a/src/util/glvnd_pthread/glvnd_pthread.h +++ b/src/util/glvnd_pthread/glvnd_pthread.h @@ -68,6 +68,9 @@ typedef struct _glvnd_thread_t { typedef pthread_attr_t glvnd_thread_attr_t; typedef pthread_rwlockattr_t glvnd_rwlockattr_t; +typedef pthread_key_t glvnd_key_t; +#define GLVND_KEYS_MAX PTHREAD_KEYS_MAX + /*! * Struct defining the wrapper functions implemented by this library. * The implementations will differ depending on whether we're in the @@ -93,6 +96,15 @@ typedef struct GLVNDPthreadFuncsRec { /* Other used functions */ int (*once)(glvnd_once_t *once_control, void (*init_routine)(void)); + + /* + * TSD key management. Used to handle the corner case when a thread + * is destroyed with a context current. + */ + int (*key_create)(glvnd_key_t *key, void (*destr_function)(void *)); + int (*key_delete)(glvnd_key_t key); + int (*setspecific)(glvnd_key_t key, const void *p); + void *(*getspecific)(glvnd_key_t key); } GLVNDPthreadFuncs; /*! |