giomodule: add a new "get default" function
authorRyan Lortie <desrt@desrt.ca>
Fri, 18 Jan 2013 23:27:57 +0000 (18:27 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Sat, 19 Jan 2013 19:04:49 +0000 (14:04 -0500)
_gio_module_get_default() is a very convenient function for modules
implementing a singleton -- it finds the default module by priority
subject to override by a given environment variable name, instantiates
it, and caches the instance for future calls.  It also has the ability
to query instances for being 'active' using a callback.

It doesn't work very well for non-singletons (like file monitors).

Add a new function _gio_module_get_default_type() that skips the
instantiation, returning the GType instead.  As a replacement for the
'active' callback, a vtable offset can be given for a virtual function
to use to query if a particular backend is supported.

https://bugzilla.gnome.org/show_bug.cgi?id=592211

gio/giomodule-priv.h
gio/giomodule.c

index c10500746495f2f1abecf12ebee51a1153e70bb9..1591c064ba3eb62dfec68b6ad19651ff777a3c72 100644 (file)
@@ -35,6 +35,10 @@ gpointer _g_io_module_get_default (const gchar         *extension_point,
                                   const gchar         *envvar,
                                   GIOModuleVerifyFunc  verify_func);
 
+GType    _g_io_module_get_default_type (const gchar *extension_point,
+                                        const gchar *envvar,
+                                        guint        is_supported_offset);
+
 #ifdef G_PLATFORM_WIN32
 void *_g_io_win32_get_module (void);
 #endif
index 74b29aab8515dabf5fa7a45c181cd9dac0ce3142..1a010ba4fcdc763928a5c676c81d2a76e8eaed6f 100644 (file)
@@ -628,8 +628,122 @@ g_io_modules_load_all_in_directory (const char *dirname)
   return g_io_modules_load_all_in_directory_with_scope (dirname, NULL);
 }
 
-GRecMutex default_modules_lock;
-GHashTable *default_modules;
+static gpointer
+try_class (GIOExtension *extension,
+           guint         is_supported_offset)
+{
+  GType type = g_io_extension_get_type (extension);
+  typedef gboolean (*verify_func) (void);
+  gpointer class;
+
+  class = g_type_class_ref (type);
+  if (!is_supported_offset || (* G_STRUCT_MEMBER(verify_func, class, is_supported_offset)) ())
+    return class;
+
+  g_type_class_unref (class);
+  return NULL;
+}
+
+/**
+ * _g_io_module_get_default_type:
+ * @extension_point: the name of an extension point
+ * @envvar: (allow-none): the name of an environment variable to
+ *     override the default implementation.
+ * @is_supported_offset: a vtable offset, or zero
+ *
+ * Retrieves the default class implementing @extension_point.
+ *
+ * If @envvar is not %NULL, and the environment variable with that
+ * name is set, then the implementation it specifies will be tried
+ * first. After that, or if @envvar is not set, all other
+ * implementations will be tried in order of decreasing priority.
+ *
+ * If @is_supported_offset is non-zero, then it is the offset into the
+ * class vtable at which there is a function that takes no arguments and
+ * returns a boolean.  This function will be called on each candidate
+ * implementation to check if it is actually usable or not.
+ *
+ * The result is cached after it is generated the first time, and
+ * the function is thread-safe.
+ *
+ * Return value: (transfer none): an object implementing
+ *     @extension_point, or %NULL if there are no usable
+ *     implementations.
+ */
+GType
+_g_io_module_get_default_type (const gchar *extension_point,
+                               const gchar *envvar,
+                               guint        is_supported_offset)
+{
+  static GRecMutex default_modules_lock;
+  static GHashTable *default_modules;
+  const char *use_this;
+  GList *l;
+  GIOExtensionPoint *ep;
+  GIOExtension *extension, *preferred;
+  gpointer impl;
+
+  g_rec_mutex_lock (&default_modules_lock);
+  if (default_modules)
+    {
+      gpointer key;
+
+      if (g_hash_table_lookup_extended (default_modules, extension_point, &key, &impl))
+        {
+          g_rec_mutex_unlock (&default_modules_lock);
+          return impl ? G_OBJECT_CLASS_TYPE (impl) : G_TYPE_INVALID;
+        }
+    }
+  else
+    {
+      default_modules = g_hash_table_new (g_str_hash, g_str_equal);
+    }
+
+  _g_io_modules_ensure_loaded ();
+  ep = g_io_extension_point_lookup (extension_point);
+
+  if (!ep)
+    {
+      g_warn_if_reached ();
+      g_rec_mutex_unlock (&default_modules_lock);
+      return G_TYPE_INVALID;
+    }
+
+  use_this = envvar ? g_getenv (envvar) : NULL;
+  if (use_this)
+    {
+      preferred = g_io_extension_point_get_extension_by_name (ep, use_this);
+      if (preferred)
+        {
+          impl = try_class (preferred, is_supported_offset);
+          if (impl)
+            goto done;
+        }
+      else
+        g_warning ("Can't find module '%s' specified in %s", use_this, envvar);
+    }
+  else
+    preferred = NULL;
+
+  for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
+    {
+      extension = l->data;
+      if (extension == preferred)
+        continue;
+
+      impl = try_class (extension, is_supported_offset);
+      if (impl)
+        goto done;
+    }
+
+  impl = NULL;
+
+ done:
+  g_hash_table_insert (default_modules, g_strdup (extension_point), impl);
+  g_rec_mutex_unlock (&default_modules_lock);
+
+  return impl ? G_OBJECT_CLASS_TYPE (impl) : G_TYPE_INVALID;
+}
 
 static gpointer
 try_implementation (GIOExtension         *extension,
@@ -684,6 +798,8 @@ _g_io_module_get_default (const gchar         *extension_point,
                          const gchar         *envvar,
                          GIOModuleVerifyFunc  verify_func)
 {
+  static GRecMutex default_modules_lock;
+  static GHashTable *default_modules;
   const char *use_this;
   GList *l;
   GIOExtensionPoint *ep;