summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBrian Nguyen <brnguyen@nvidia.com>2013-11-21 14:02:20 -0800
committerbrnguyen <brnguyen@nvidia.com>2013-11-26 18:38:04 -0800
commit7a7d49bf0c8263c5f25be96a7fda2ffe78942112 (patch)
tree0165adea65c36f4ab0989b0fd7f7da75d8c5b0b9 /src
parent92dfd9c5fe8f5f169c6bba7cb87b2389bb8780c6 (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.c73
-rw-r--r--src/GLX/libglxmapping.c6
-rw-r--r--src/GLX/libglxthread.h2
-rw-r--r--src/util/glvnd_pthread/glvnd_pthread.c57
-rw-r--r--src/util/glvnd_pthread/glvnd_pthread.h12
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;
/*!