From ddd5f2a9791bbd938ce79470844110d07e8dea69 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Nicolai=20H=C3=A4hnle?= Date: Fri, 30 Jun 2017 11:04:49 +0200 Subject: [PATCH] glx: use v2 of DRI_ConfigOptions MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 --- src/glx/dri_glx.c | 113 ++++++++++++++++++++++++++++++++++++++++------ 1 file 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 -- 2.30.2