diff options
author | Nicolai Hähnle <nicolai.haehnle@amd.com> | 2017-06-30 11:04:49 +0200 |
---|---|---|
committer | Nicolai Hähnle <nicolai.haehnle@amd.com> | 2017-07-13 13:27:44 +0200 |
commit | 203acc677e138c9b47cbefb4d80d3e8f55835c71 (patch) | |
tree | 995b94b67a1290bdea436d5cb224fd1a8c767fd7 | |
parent | 950a7ce09ec8a893435c7afb738a7f65dfa89f4c (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.
-rw-r--r-- | src/glx/dri_glx.c | 113 |
1 files changed, 99 insertions, 14 deletions
diff --git a/src/glx/dri_glx.c b/src/glx/dri_glx.c index ae7e11c174..2d435f0ee1 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 |