GNOME Bugzilla – Bug 51583
reimplemented gmodule as a wrapper for (soon-to-be) libtool-1.4's libltdl
Last modified: 2012-01-16 03:36:58 UTC
gmodule.h: /* GMODULE - GLIB wrapper code for dynamic module loading * Copyright (C) 1998 Tim Janik * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * Modified by the GLib Team and others 1997-2000. See the AUTHORS * file for a list of people on the GLib Team. See the ChangeLog * files for a list of changes. These files are distributed with * GLib at ftp://ftp.gtk.org/pub/gtk/. */ #ifndef __GMODULE_H__ #define __GMODULE_H__ extern const char *g_log_domain_gmodule; #include <glib.h> G_BEGIN_DECLS /* exporting and importing functions, this is special cased * to feature Windows dll stubs. */ #define G_MODULE_IMPORT extern #if defined (G_OS_WIN32) # define G_MODULE_EXPORT __declspec(dllexport) #else /* !G_OS_WIN32 */ # define G_MODULE_EXPORT #endif /* !G_OS_WIN32 */ /* No longer used. These remain for interface compatibility only. */ typedef enum { G_MODULE_BIND_LAZY = 1 << 0, G_MODULE_BIND_MASK = 0x01 } GModuleFlags; typedef gpointer GModule; typedef const gchar* (*GModuleCheckInit) (GModule *module); typedef void (*GModuleUnload) (GModule *module); /* open a module `file_name' and return handle, which is NULL on error */ GModule* g_module_open (const gchar *file_name, GModuleFlags flags); /* close a previously opened module, returns TRUE on success */ gboolean g_module_close (GModule *module); /* make a module resident so g_module_close on it will be ignored */ void g_module_make_resident (GModule *module); /* query the last module error as a string */ gchar* g_module_error (void); /* retrive a symbol pointer from `module', returns TRUE on success */ gboolean g_module_symbol (GModule *module, const gchar *symbol_name, gpointer *symbol); /* retrive the file name from an existing module */ gchar* g_module_name (GModule *module); /* Reset the list of colon separated module search directories. */ gchar* g_module_set_search_path (gchar *path); /* Add a new search directory to the list. */ gchar* g_module_add_search_path (gchar *path); /* --- deprecated functions --- */ gboolean g_module_supported (void) G_GNUC_CONST; gchar* g_module_build_path (const gchar *directory, const gchar *module_name); G_END_DECLS #endif /* __GMODULE_H__ */ gmodule.c: /* GMODULE - GLIB wrapper code for dynamic module loading * Copyright (C) 1998 Tim Janik * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * Modified by the GLib Team and others 1997-2000. See the AUTHORS * file for a list of people on the GLib Team. See the ChangeLog * files for a list of changes. These files are distributed with * GLib at ftp://ftp.gtk.org/pub/gtk/. */ #include "gmodule.h" #include "gmoduleconf.h" #include "ltdl.h" #include <errno.h> #include <string.h> #define G_CALLERID_UNDEFINED ((lt_dlcaller_id) -1) #define G_MODULE_INI_STRING "g_module_check_init" #define G_MODULE_FINI_STRING "g_module_unload" /* --- prototypes --- */ static inline gboolean g_module_init_once (void); static inline void g_module_set_error (const gchar *error); static const gchar *g_module_get_error (void); static void g_module_mutex_lock (void); static void g_module_mutex_unlock (void); /* --- variables --- */ G_LOCK_DEFINE_STATIC (GModule); const char *g_log_domain_gmodule = "GModule"; static GStaticPrivate module_error_private = G_STATIC_PRIVATE_INIT; /* --- inline functions --- */ static inline void g_module_set_error (const gchar *error) { g_static_private_set (&module_error_private, error, NULL); errno = 0; } /* For orthogonality only: g_module_error already does exactly what is required, but is named according to the supported API. */ #define g_module_get_error g_module_error static inline gboolean g_module_init_once (void) { static gint initialized = 0; /* Clear error message for this thread. */ g_module_set_error (NULL); G_LOCK (GModule); if (++initialized == 1) { gint errors = 0; /* Number of errors from libltdl calls. */ /* Make sure libltdl is using our memory managment API. */ lt_dlmalloc = (lt_ptr(*)(size_t)) g_malloc; lt_dlfree = (void (*)(lt_ptr)) g_free; /* Register the callbacks to make all lt_*() functions MT safe. */ lt_dlmutex_register (g_module_mutex_lock, g_module_mutex_unlock, g_module_set_error, g_module_get_error); /* Initialise ltdl library. */ errors = lt_dlinit (); if (!errors) { gchar *path = g_getenv ("GMODULE_PATH"); /* Create a gmodule specific key for subsequent access to per-module data stored by gmodule functions. */ g_module_caller_id = lt_dlcaller_register (); /* Initialise libltdl module search directory list. */ if (path != NULL) errors = lt_dladdsearchdir (path); } G_UNLOCK (GModule); return (errors == 0); } G_UNLOCK (GModule); return TRUE; } /* --- static functions --- */ static void g_module_mutex_lock (void) { G_LOCK (GModule); } static void g_module_mutex_unlock (void) { G_UNLOCK (GModule); } /* --- exported functions --- */ /** * g_module_supported: * * This function is deprecated. * Check if modules are supported on the current platform. Now that * the gmodule API is a wrapper for libltdl, this function will always * return %TRUE. * * Return value: %TRUE if modules are supported. **/ gboolean g_module_supported (void) { #if G_ENABLE_DEBUG static gboolean first_call = TRUE; if (first_call) { g_warning ("g_module_supported is deprecated."); g_warning ("modules are always supported by the underlying libltdl."); first_call = FALSE; } #endif /* G_ENABLE_DEBUG */ return TRUE; } /** * g_module_open: * @filename: the name of the file containing the module to be opened. * @flags: not used. * * Opens a module. If the module has already been opened, its reference * count is incremented. * * First of all g_module_open() tries to open @file_name as a module. If * that fails and @file_name has the ".la"-suffix (and is a libtool archive) * it tries to open the corresponding module. If that fails and @file_name * doesn't have the proper module suffix for the host platform, this suffix * will be appended and any corresponding module opened. If that fails and * @file_name doesn't have the ".la"-suffix, it is appended and * g_module_open() tries to open the corresponding module. If ultimately * that fails as well, %NULL is returned. * * Return value: a #GModule on success, or %NULL on failure. **/ GModule* g_module_open (const gchar *file_name, GModuleFlags flags) { lt_dlhandle handle = (lt_dlhandle) 0; g_module_init_once (); /* open the module */ handle = lt_dlopenext (file_name); if (handle) { gchar *error = NULL; GModuleCheckInit check_init; GModuleUnload unload; const gchar *check_failed_error = NULL; /* check initialization */ if (g_module_symbol ((GModule *) handle, G_MODULE_INI_STRING, (gpointer) &check_init)) { check_failed_error = check_init ((GModule *) handle); } if (check_failed_error != NULL) { error = g_strconcat ("GModule initialization check failed: ", check_failed_error, NULL); } /* Register error diagnostic generated above, if any. */ if (error) { g_module_close ((GModule *) handle); module = NULL; g_module_set_error (error); g_free (error); } } return module; } /** * g_module_close: * @module: the module to be closed. * * Closes an open module. * * Return value: %TRUE on success. **/ gboolean g_module_close (GModule *module) { lt_dlinfo *info; gboolean success = TRUE; g_return_val_if_fail (module != NULL, FALSE); g_module_init_once (); /* Is this really neccessary? */ info = lt_dlgetinfo ((lt_dlhandle) module); g_return_val_if_fail (info->ref_count > 1, FALSE); if ((info->ref_count == 1) && !lt_dlisresident ((lt_dlhandle) module)) { GModuleUnload unload; lt_ptr stale = NULL; unload = g_module_symbol (module, G_MODULE_FINI_STRING, (gpointer) &unload); if (unload) unload (module); if (lt_dlclose ((lt_dlhandle) module) != 0) success = FALSE; g_free (module); } return success; } /** * g_module_make_resident: * @module: the module to make permenantly resident. * * Ensures that a module can never be unloaded. Any future g_module_close() * calls on the module will have no effect. If you open the entire process * as a reflexive module, then it is always made resident by default -- there * is no need to call this function manually. **/ void g_module_make_resident (GModule *module) { g_return_if_fail (module != NULL); lt_dlmakeresident ((lt_dlhandle) module); } /** * g_module_error: * * Get a string describing the last error that occured in the gmodule * subsystem. * * Return value: a string describing the last module error. **/ gchar* g_module_error (void) { return g_static_private_get (&module_error_private); } /** * g_module_symbol: * @module: the module. * @symbol_name: the name of the symbol to find. * @symbol: returns the pointer to the symbol value. * * Gets a symbol pointer from a module. * * Return value: %TRUE on success. **/ gboolean g_module_symbol (GModule *module, const gchar *symbol_name, gpointer *symbol) { if (symbol) *symbol = NULL; g_return_val_if_fail (module != NULL, FALSE); g_return_val_if_fail (symbol_name != NULL, FALSE); g_return_val_if_fail (symbol != NULL, FALSE); *symbol = lt_dlsym ((lt_dlhandle) module, symbol_name); if (*symbol == NULL) { gchar *module_error = lt_dlerror (); gchar *error; error = g_strconcat ("`", symbol_name, "': ", module_error, NULL); g_module_set_error (error); g_free (error); return FALSE; } return TRUE; } /** * g_module_name: * @module: the module. * * Gets the file name from a #GModule. * * Return value: the file name of the module, or "main" if the module * is the main program itself. **/ gchar* g_module_name (GModule *module) { lt_dlinfo *info; g_return_val_if_fail (module != NULL, NULL); info = lt_dlgetinfo ((lt_dlhandle) module); return g_strdup (info->filename ? info->filename : "main"); } /** * g_module_set_search_path: * @path: a colon delimited list of directories. * * Set the paths of the directories to be searced when trying to * locate a module opened using a relative path. * * Return value: %TRUE if successful. **/ gboolean g_module_set_search_path (gchar *path) { return (lt_dlsetsearchpath (path) == 0); } /** * g_module_add_search_path: * @path: a colon delimited list of directories. * * Append to the list of user paths, the additional @path of directories * to be searched when trying to locate a module opened using a * relative path. * * Return value: %TRUE if successful. **/ gboolean g_module_add_search_path (gchar *path) { return (lt_dladdsearchdir (path) == 0); } /** * g_module_build_path: * @directory: the directory where the module is. * @module_name: the name of the module. * * This function is deprecated. **/ gchar* g_module_build_path (const gchar *directory, const gchar *module_name) { #if G_ENABLE_DEBUG static gboolean first_call = TRUE; if (first_call) { g_warning ("g_module_build_path is deprecated."); g_warning ("Use g_module_set_search_path or g_module_add_search_path."); first_call = FALSE; } #endif /* G_ENABLE_DEBUG */ #if (G_MODULE_IMPL == G_MODULE_IMPL_DL) if (directory && *directory) { if (strncmp (module_name, "lib", 3) == 0) return g_strconcat (directory, "/", module_name, NULL); else return g_strconcat (directory, "/lib", module_name, ".so", NULL); } else if (strncmp (module_name, "lib", 3) == 0) return g_strdup (module_name); else return g_strconcat ("lib", module_name, ".so", NULL); #elif (G_MODULE_IMPL == G_MODULE_IMPL_DLD) if (directory && *directory) if (strncmp (module_name, "lib", 3) == 0) return g_strconcat (directory, "/", module_name, NULL); else return g_strconcat (directory, "/lib", module_name, ".sl", NULL); else if (strncmp (module_name, "lib", 3) == 0) return g_strdup (module_name); else return g_strconcat ("lib", module_name, ".sl", NULL); #elif (G_MODULE_IMPL == G_MODULE_IMPL_BEOS) g_warning ("g_module_build_path() untested for BeOS!"); if (directory && *directory) { if (strncmp (module_name, "lib", 3) == 0) return g_strconcat (directory, "/", module_name, NULL); else return g_strconcat (directory, "/lib", module_name, ".so", NULL); } else if (strncmp (module_name, "lib", 3) == 0) return g_strdup (module_name); else return g_strconcat ("lib", module_name, ".so", NULL); #elif (G_MODULE_IMPL == G_MODULE_IMPL_WIN32) { gint k = strlen (module_name); if (directory && *directory) if (k > 4 && g_strcasecmp (module_name + k - 4, ".dll") == 0) return g_strconcat (directory, "\\", module_name, NULL); else return g_strconcat (directory, "\\", module_name, ".dll", NULL); else if (k > 4 && g_strcasecmp (module_name + k - 4, ".dll") == 0) return g_strdup (module_name); else return g_strconcat (module_name, ".dll", NULL); } #elif (G_MODULE_IMPL == G_MODULE_IMPL_OS2) { gchar *suffix = strrchr(module_name, '.'); if (directory && *directory) if (suffix && (stricmp (suffix, ".dll") == 0)) return g_strconcat (directory, "/", module_name, NULL); else return g_strconcat (directory, "/", module_name, ".dll", NULL); else if (suffix && (stricmp (suffix, ".dll") == 0)) return g_strdup (module_name); else return g_strconcat (module_name, ".dll", NULL); } #endif return NULL; }
Created attachment 354 [details] [review] gmodule.h
Created attachment 355 [details] [review] gmodule.c
My first bugzilla submission... Please excuse the original long description, There was no obvious way to submit the files until after I returned to the bug. Sorry. Anyway, I redid the implementation of gmodule as a wrapper for libltdl to take advantage of the thread locking hooks recently committed to libtool CVS, so that we can hopefully unify the two code bases. The last few months have occupied me with pushing features gmodule had down into libltdl to make this possible, so the wrapper is actually very thin. The purpose of this report is mainly to get some feedback -- I haven't actually tested this code at all. In fact, I have never programmed with threads in C before, so some pointers to a small threaded gmodule using project would be a huge help! Let me know what you think and if you approve I will incorporate any suggestions you have, clean the whole thing up and resubmit.
I'm putting this on the 2.0 milestone temporarily, but I suspect that we'll have to push it off past that, since I don't think we have time at this point to properly evaluate and test this change. I'm also nervous about adding another run-time /compile-time dependency to the GTK+ system; we've already upped the ante-there quite a bit for GTK+-2.0. For libfribidi we decided that having an external dependency was too painful, and we just moved the relevant portion of the code into Pango. Finally, if gmodule should become just a wrapper for libltdl (and doesn't have a different implementation on any platform) then perhaps the right thing to do (long-term) is to simply deprecate gmodule in favor of using libltdl directly. That's my abstract thoughts on the issue - I have not looked at the patch or libltdl yet.
To address your concerns with another compile time dependency: libltdl ships with libtool and the libltdl code is copied into your project source tree at libtoolize time (much like gettextize adds the internationalisation source code). Deprecation gmodule is a smashing idea, though I'd like to see a source compatibility wrapper modelled after this patch for the next stable release or two for the sake of projects that already use the gmodule API. I am happy to do the work on integration for this patch if I get an approval in principle.
Neither gettextize or libtoolize work in the context of a library like glib... glib has no business installing a libltdl.so and certainly can't link to it statically. I'm putting this on a future milestone for consideration again later; right now, I don't see this as something that actually gives tangible benefits for users, but certainly the reduction of duplication for users would be good. In any case, it's a too big change to consider for 2.0.x.
i would really like to see this happen. at this point we actually need a new patch that targets libtool-1.5. as far as the libltdl functions are concerned though, those can be renamed and integrated completely into glib, without interferance of any other installed libraries. at least last time i looked at libltdl, the code allowed for that. i don't think full deprecation of the g_module API is a good idea then, i found the libltdl api crufty to use at best, and don't see a necessity for applications to port away from a g_module api if it works correctly.
Does libltdl on Win32 implement the required symbol lookup functionality as gmodule-win32 does (using two alternative APIs even, to work also on NT 4)? I.e. look for the symbol in all currently loaded DLLs *and* the exe?
I think it is clear that this is not going to happen