summaryrefslogtreecommitdiffstats
path: root/src/glx
diff options
context:
space:
mode:
authorNicolai Hähnle <[email protected]>2017-06-30 11:04:49 +0200
committerNicolai Hähnle <[email protected]>2017-08-02 09:50:56 +0200
commitddd5f2a9791bbd938ce79470844110d07e8dea69 (patch)
treecea4e780f53e6eaf5b19f380245f3c88c1af37a4 /src/glx
parent9435b9c544ec9b8339f31fc89322a9b88af36af5 (diff)
glx: use v2 of DRI_ConfigOptions
Most of the change is concerned with avoiding memory leaks, since v2 of the DRI extension returns a malloc'ed string. This also allows us to resolve the long-standing issue of keeping drivers loaded when returning from glXGetDriverConfig. Reviewed-by: Marek Olšák <[email protected]>
Diffstat (limited to 'src/glx')
-rw-r--r--src/glx/dri_glx.c113
1 files changed, 99 insertions, 14 deletions
diff --git a/src/glx/dri_glx.c b/src/glx/dri_glx.c
index ae7e11c174b..2d435f0ee19 100644
--- a/src/glx/dri_glx.c
+++ b/src/glx/dri_glx.c
@@ -167,6 +167,76 @@ glXGetScreenDriver(Display * dpy, int scrNum)
return NULL;
}
+/* glXGetDriverConfig must return a pointer with a static lifetime. To avoid
+ * keeping drivers loaded and other leaks, we keep a cache of results here that
+ * is cleared by an atexit handler.
+ */
+struct driver_config_entry {
+ struct driver_config_entry *next;
+ char *driverName;
+ char *config;
+};
+
+static pthread_mutex_t driver_config_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct driver_config_entry *driver_config_cache = NULL;
+
+/* Called as an atexit function. Otherwise, this would have to be called with
+ * driver_config_mutex locked.
+ */
+static void
+clear_driver_config_cache()
+{
+ while (driver_config_cache) {
+ struct driver_config_entry *e = driver_config_cache;
+ driver_config_cache = e->next;
+
+ free(e->driverName);
+ free(e->config);
+ free(e);
+ }
+}
+
+static char *
+get_driver_config(const char *driverName)
+{
+ void *handle = driOpenDriver(driverName);
+ const __DRIextension **extensions;
+
+ if (!handle)
+ return NULL;
+
+ char *config = NULL;
+
+ extensions = driGetDriverExtensions(handle, driverName);
+ if (extensions) {
+ for (int i = 0; extensions[i]; i++) {
+ if (strcmp(extensions[i]->name, __DRI_CONFIG_OPTIONS) != 0)
+ continue;
+
+ __DRIconfigOptionsExtension *ext =
+ (__DRIconfigOptionsExtension *)extensions[i];
+
+ if (ext->base.version >= 2)
+ config = ext->getXml(driverName);
+ else
+ config = strdup(ext->xml);
+
+ break;
+ }
+ }
+
+ if (!config) {
+ /* Fall back to the old method */
+ config = dlsym(handle, "__driConfigOptions");
+ if (config)
+ config = strdup(config);
+ }
+
+ dlclose(handle);
+
+ return config;
+}
+
/*
* Exported function for obtaining a driver's option list (UTF-8 encoded XML).
*
@@ -175,28 +245,43 @@ glXGetScreenDriver(Display * dpy, int scrNum)
*
* If the driver was not found or does not support configuration NULL is
* returned.
- *
- * Note: The driver remains opened after this function returns.
*/
_GLX_PUBLIC const char *
glXGetDriverConfig(const char *driverName)
{
- void *handle = driOpenDriver(driverName);
- const __DRIextension **extensions;
+ struct driver_config_entry *e;
- if (!handle)
- return NULL;
+ pthread_mutex_lock(&driver_config_mutex);
- extensions = driGetDriverExtensions(handle, driverName);
- if (extensions) {
- for (int i = 0; extensions[i]; i++) {
- if (strcmp(extensions[i]->name, __DRI_CONFIG_OPTIONS) == 0)
- return ((__DRIconfigOptionsExtension *)extensions[i])->xml;
- }
+ for (e = driver_config_cache; e; e = e->next) {
+ if (strcmp(e->driverName, driverName) == 0)
+ goto out;
}
- /* Fall back to the old method */
- return dlsym(handle, "__driConfigOptions");
+ e = malloc(sizeof(*e));
+ if (!e)
+ goto out;
+
+ e->config = get_driver_config(driverName);
+ e->driverName = strdup(driverName);
+ if (!e->config || !e->driverName) {
+ free(e->config);
+ free(e->driverName);
+ free(e);
+ e = NULL;
+ goto out;
+ }
+
+ e->next = driver_config_cache;
+ driver_config_cache = e;
+
+ if (!e->next)
+ atexit(clear_driver_config_cache);
+
+out:
+ pthread_mutex_unlock(&driver_config_mutex);
+
+ return e ? e->config : NULL;
}
static GLboolean