From 3a7b44c007c2d80d9adfa883eca82124ec39deee Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Fri, 18 Jan 2013 18:27:57 -0500 Subject: [PATCH] giomodule: add a new "get default" function _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 | 4 ++ gio/giomodule.c | 120 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 122 insertions(+), 2 deletions(-) diff --git a/gio/giomodule-priv.h b/gio/giomodule-priv.h index c10500746..1591c064b 100644 --- a/gio/giomodule-priv.h +++ b/gio/giomodule-priv.h @@ -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 diff --git a/gio/giomodule.c b/gio/giomodule.c index 74b29aab8..1a010ba4f 100644 --- a/gio/giomodule.c +++ b/gio/giomodule.c @@ -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;