[Mesa-dev] [PATCH 02/16] glx: use v2 of DRI_ConfigOptions

Nicolai Hähnle nhaehnle at gmail.com
Fri Jun 30 12:45:43 UTC 2017


From: Nicolai Hähnle <nicolai.haehnle at amd.com>

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.
---
 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 ae7e11c..2d435f0 100644
--- a/src/glx/dri_glx.c
+++ b/src/glx/dri_glx.c
@@ -160,50 +160,135 @@ glXGetScreenDriver(Display * dpy, int scrNum)
       len = strlen(driverName);
       if (len >= 31)
          return NULL;
       memcpy(ret, driverName, len + 1);
       free(driverName);
       return ret;
    }
    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).
  *
  * The returned char pointer points directly into the driver. Therefore
  * it should be treated as a constant.
  *
  * 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
 has_damage_post(Display * dpy)
 {
    static GLboolean inited = GL_FALSE;
    static GLboolean has_damage;
 
    if (!inited) {
       int major, minor;
-- 
2.9.3



More information about the mesa-dev mailing list