glx: use v2 of DRI_ConfigOptions
authorNicolai Hähnle <nicolai.haehnle@amd.com>
Fri, 30 Jun 2017 09:04:49 +0000 (11:04 +0200)
committerNicolai Hähnle <nicolai.haehnle@amd.com>
Wed, 2 Aug 2017 07:50:56 +0000 (09:50 +0200)
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 <marek.olsak@amd.com>
src/glx/dri_glx.c

index ae7e11c174b7f9dddfe43f8c4667f935b5971460..2d435f0ee192700680f72941ec8b01412b1a3329 100644 (file)
@@ -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