Re: revised patch for glib compilation



On Tuesday 20 March 2001  4:29 am, Tim Janik wrote:
> On Tue, 20 Mar 2001, Gary V. Vaughan wrote:
> > Hi Tim,
> >
> > Thanks for the feedback.  I have addressed all but one of your comments
> > in the attached files -- if you could help me to understand how to
> > resolve the remaining one, I'll do that and attach the result to
> > bugzilla.  [[Aside: assuming it may take a number of months before this
> > stuff arrives in CVS, I'm guessing that directly attaching the files is
> > preferable to a large bitrotting patch file?]]
>
> it's easier for me to have the patch included in the mail text actually,
> that way i can just hit Reply and insert comments, with attachments i need
> to save+load those and add quote chars ;)

I was referring to bugzilla attachments...

> > I can see the
> > need for a recursive lock, but I have no idea of how to express that in
> > terms of the macros in CVS HEAD gthread.h.  Is there an example of
> > recursive G_LOCKing on the `net I can look at?
>
> well, the best is probably gmodule/gmodule.c ;)

Right.  Got it.  Thanks!

> i wouldn't mind this memory not showing up in mem-profile, however i don't
> have a strong opinion either way.

Me neither.  I've left it in, but feel free to remove it when the patch is 
applied.

> > > also you don't do any reference counting in GModule itself
> > > anymore which is a problem wrg track-keeping of the init function.
> >
> > libltdl keeps track of the reference count for me.  I can take a look at
> > the current count with:
> >
> >     lt_dlinfo *info       = lt_dlgetinfo (handle);
> >     gint       ref_count  = info->ref_count;
>
> yes you can, no doubt about that, but you still need to do the
> decrement somewhere ;)

Nope.  libltdl exposes its own reference count as a read only value.  From 
ltdl.c:

  int
  lt_dlclose (handle)
       lt_dlhandle handle;
  {
    lt_dlhandle cur, last;
    int errors = 0;
 
    MUTEX_LOCK ();
 
    /* check whether the handle is valid */
    last = cur = handles;
    while (cur && handle != cur)
      {
        last = cur;
        cur = cur->next;
      }
   
    if (!cur)
      {
        MUTEX_SETERROR (LT_DLSTRERROR (INVALID_HANDLE));
        ++errors;
        goto done;
      }
 
    handle->info.ref_count--;

    ...

> i.e. a program doing:
>
> g_module_open ("foo");
> h = g_module_open ("foo");
> g_module_close (h);
> g_module_close (h);
>
> has this effect:
>
> first     g_module_open ("foo"): load module into memory;
>                                  if (g_module_init() != NULL)
>                                    throw module out of mem;
>                                  else
>                                    module("foo")->ref_count = 1
> second    g_module_open ("foo"): module("foo")->ref_count += 1;
> first     g_module_close (h)   : module("foo")->ref_count -= 1;
> second    g_module_close (h)   : module("foo")->ref_count -= 1;
>                                  if (ref_count == 0 && !resident)
>                                    g_module_unload();
>                                  if (!resident)
>                                    throw module out of memory;
> so open() after a successfull open() acts as ref_count+=1 and close()
> simply acts as ref_count-=1 untill 0 is reached.
> when 0 is we do the unload call and actually unload the module if
> it wasn't made resident. not the extra check for residency after
> g_module_unload(), because that funciton may still make the
> module resident.

Hmm.  You're right.  It is the call to lt_dlclose which decrements the 
reference count, and the old flow doesn't reach it under the right 
circumstances.  The attached version does fix it however.  Thanks.

> > > how's libtool going to handle "libfoo.la" vs. "libfoo.so" vs.
> > > "libfoo.dll" vs. "libfoo" vs. "foo" arguments being passed into open()?
> >
> > lt_dlopenext("libfoo") (as used by g_module_open()) adds the host
> > platform's module extension, if dlopening of the exact argument string
> > failed.
>
> well, the current build path code adds "lib" prefixes on demand as well...

Actually, that was a poor example.  Some platforms (the BSD derived 
Darwin/MacOS X springs to mind) produce different incompatible objects for 
loadable modules compared to a runtime shared library, even from the same 
code.  This means that it is not portable to tack "lib" onto a module name 
that could not be found and search again, since you may end up trying to load 
a non-dlopenable object -- generating spurious errors at best.

I think it is fair to assume that the application and/or module author should 
know the name of the module they want to load, and can arrange for the full 
name to be passed in to g_module_open()...

Anyhow, gmodule.h is unchanged from my last post, and the updates to 
gmodule.c are attached (mostly the recursive mutex, and the revised 
g_module_close() implementation).  My mailer tries to refill the code if I 
paste it into the mail directly, so I am attaching as before.  Sorry :(

If you think this is okay, I'll file a complete patch to update configure.in 
and autogen.sh along with these updates as soon as libtool-1.4 is released.  
Out of interest do you check in autogenerated files (libintl sources, libltdl 
sources etc) to CVS?

I'm very excited by this, and looking forward to being able to use glib for 
the next release of GNU M4. =)O|

Cheers,
	Gary.
-- 
  ___              _   ___   __              _         mailto: gvv techie com
 / __|__ _ _ ___ _| | / / | / /_ _ _  _ __ _| |_  __ _ ___       gary gnu org
| (_ / _` | '_|// / |/ /| |/ / _` | || / _` | ' \/ _` | _ \
 \___\__,_|_|\_, /|___(_)___/\__,_|\_,_\__, |_||_\__,_|//_/
home page:  /___/                      /___/                  gpg public key:
http://www.oranda.demon.co.uk           http://www.oranda.demon.co.uk/key.asc
/* 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_INIT_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 --- */
static GStaticRecMutex g_module_global_lock = G_STATIC_REC_MUTEX_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 gboolean initialized = FALSE;

  /* Clear error message for this thread.  */
  g_module_set_error (NULL);

  g_static_rec_mutex_lock (&g_module_global_lock);
  if (!initialized)
    {
      gint errors = 0;		/* Number of errors from libltdl calls.  */

      initialized = TRUE;	/* No need to do this again.  */

      /* 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_static_rec_mutex_unlock (&g_module_global_lock);
      return (errors == 0);
    }

  g_static_rec_mutex_unlock (&g_module_global_lock);
  return TRUE;
}

/* --- static functions --- */
static void
g_module_mutex_lock (void)
{
  g_static_rec_mutex_lock (&g_module_global_lock);
}

static void
g_module_mutex_unlock (void)
{
  g_static_rec_mutex_unlock (&g_module_global_lock);
}


/* --- 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. "
		 "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;
  gchar	      *error  = NULL;
  lt_dlinfo   *info   = NULL;
  
  g_module_init_once ();

  g_static_rec_mutex_lock (&g_module_global_lock);

  /* open the module */
  handle = lt_dlopenext (file_name);
  
  if (handle)
    info = lt_dlgetinfo (handle);
  
  /* Don't try to run the _init function, except when this is the first
   * time this module has been loaded.
   */
  if (info && (info->ref_count == 1))
    {
      GModuleCheckInit check_init;
      GModuleUnload unload;
      const gchar *check_failed_error = NULL;
      
      /* check initialization */
      if (g_module_symbol ((GModule *) handle, G_MODULE_INIT_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);
	}
    }
  
  g_static_rec_mutex_unlock (&g_module_global_lock);

  /* Register error diagnostic generated above, if any. */
  if (error)
    {
      g_module_close ((GModule *) handle);
      handle = (lt_dlhandle) 0;
      g_module_set_error (error);
      g_free (error);
    }

  return (GModule *) handle;
}

/**
 * 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);

  info = lt_dlgetinfo ((lt_dlhandle) module);

  g_return_val_if_fail (info->ref_count > 1, FALSE);
  

  /* If the module is not resident, and the reference count is exactly 1,
   * find and execute the module's unload function (if any).
   */
  if ((info->ref_count == 1) && !lt_dlisresident ((lt_dlhandle) module))
    {
      GModuleUnload unload = g_module_symbol (module, G_MODULE_FINI_STRING,
					      (gpointer) &unload);

      if (unload)
	unload (module);
    }
  
  /* Always call lt_dlclose() to ensure the reference count is maintained
   * correctly -- the module will not actually be unloaded if the reference
   * count remains above zero, or if the module was marked resident.
   */
  if (lt_dlclose ((lt_dlhandle) module) != 0)
    success = FALSE;

  /* The call to unload may have made a previously non-resident module 
   * resident, in which case the module remains in memory (with a
   * ref_count <= 0).
   */
  if (!lt_dlisresident ((lt_dlhandle) module))
    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.
 **/
const gchar*
g_module_name (GModule *module)
{
  lt_dlinfo  *info;
  
  g_return_val_if_fail (module != NULL, NULL);
  
  info   = lt_dlgetinfo ((lt_dlhandle) module);

  return (const gchar *) (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. "
		 "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;
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]