m4-patches
[Top][All Lists]
Advanced

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

module cleanup [1/n]


From: Eric Blake
Subject: module cleanup [1/n]
Date: Thu, 6 Sep 2007 22:56:45 +0000 (UTC)
User-agent: Loom/3.14 (http://gmane.org/)

While investigating the possibility of tracing builtins regardless of the name 
used to invoke them (or lack thereof, as in builtin(`divnum'))[1], I noticed a 
number of (potential) issues with modules (not all of them fixable in M4):

- Right now, load(nosuchmodule) and unload(nosuchmodule) both exit the 
program.  Maybe they should give a warning, but continue processing input?

- The current module interface uses data exports for the builtin and macro 
tables.  Libtool recommends using function exports instead of data exports; 
changing this would be a change in the module interface, but might make porting 
to obscure platforms easier

- Based on the above point, it would be nice if we made ALL m4-compatible 
modules export a function that advertised what version of m4 module interface 
they comply with.  Absence of the version query function implies interface 
version 1 (ie. the current version), where at least one of the builtin table, 
macro table, init function, or finish function must exist.  Then, we can do a 
better job about supporting new features in new modules, while still being able 
to load old modules

- There is an open issue about freezing module-dependent state, such as the m4 
module saving the current state of the sysval macro, which would require a new 
API - having a module interface versioning scheme would make this nicer to 
implement

- Module ref-counting is screwy.  The current unload code assumes that builtins 
associated with a module must only be unloaded once the module refcount is at 
one and will be dropping to 0.  But that's a bit presumptuous, since the 
libltdl ref-count could include 3rd party loads of the same module that should 
have no impact to our symbol table.  Also, we weren't decreasing the ref-count 
of resident modules, which meant that symbols from such a module weren't always 
being removed from the symbol table at the right time

- Speaking of refcounts, the m4modules builtin only shows a module once, even 
if it has been loaded multiple times.  Maybe it is worth adding another builtin 
to the load module that can query the current refcount of a given module name?

- We aren't very robust to modules that change their mind midstream.  It is 
currently possible for a stateful module to crash m4, by altering the array 
that the exported builtin table points at based on what actions are done via 
module builtins.  Also, I'm not sure how expensive lt_dlsym is, but do know 
that system calls are more expensive than memory checks.  So rather than re-
query lt_dlsym every time we need to search the module for a particular 
builtin, it would be more robust to copy and cache the builtin table provided 
by the module when it was first loaded, then refer to the cache thereafter

- If we add symbol caching, then it becomes trivial to store information 
associated with a builtin (back to my original thought of adding the ability to 
trace a builtin regardless of the name it is invoked by); without a cache, 
there is no convenient place to do per-builtin tracking since we can't 
necessarily write into the memory owned by a module

- We currently expose the libltdl interface to all clients of m4module.h.  That 
is poor interface design

- Error messages from libltdl are not translated


I'm starting a patch series to try to improve several points; the series may be 
a bit open-ended based on what else I discover.  Fortunately, it only applies 
to head and I don't have to worry about ports back to the branch.  This first 
patch converts the m4module.h interface for m4 modules away from lt_dlhandle 
and into an m4-specific opaque type (right now, the type merely holds the 
lt_dlhandle that it wraps, and the lt_dlhandle holds the pointer back to the 
opaque type, and it relies on my libtool fix from yesterday), and improves (but 
not solves) ref-count handling of resident modules.  I didn't see any way to 
split this particular patch into smaller chunks.  I'm applying this:

Index: m4/builtin.c
===================================================================
RCS file: /sources/m4/m4/m4/builtin.c,v
retrieving revision 1.29
diff -u -p -r1.29 builtin.c
--- m4/builtin.c        7 Aug 2007 03:15:26 -0000       1.29
+++ m4/builtin.c        6 Sep 2007 22:21:45 -0000
@@ -30,14 +30,14 @@
    symbol value, suitable for use in the symbol table or for an
    argument to m4_push_builtin.  */
 m4_symbol_value *
-m4_builtin_find_by_name (lt_dlhandle handle, const char *name)
+m4_builtin_find_by_name (m4_module *handle, const char *name)
 {
-  lt_dlhandle cur = handle ? handle : m4__module_next (0);
+  m4_module *cur = handle ? handle : m4__module_next (NULL);
 
   do
     {
       const m4_builtin *builtin =
-       (m4_builtin *) lt_dlsym (cur, BUILTIN_SYMBOL);
+       (m4_builtin *) lt_dlsym (cur->handle, BUILTIN_SYMBOL);
 
       if (builtin)
        {
@@ -63,14 +63,14 @@ m4_builtin_find_by_name (lt_dlhandle han
 /* Find the builtin which has FUNC.  If HANDLE argument is supplied
    then search only in HANDLE's builtin table.  */
 const m4_builtin *
-m4_builtin_find_by_func (lt_dlhandle handle, m4_builtin_func *func)
+m4_builtin_find_by_func (m4_module *handle, m4_builtin_func *func)
 {
-  lt_dlhandle cur = handle ? handle : m4__module_next (0);
+  m4_module *cur = handle ? handle : m4__module_next (NULL);
 
   do
     {
       const m4_builtin *builtin =
-       (const m4_builtin *) lt_dlsym (cur, BUILTIN_SYMBOL);
+       (const m4_builtin *) lt_dlsym (cur->handle, BUILTIN_SYMBOL);
 
       if (builtin)
        {
Index: m4/m4module.h
===================================================================
RCS file: /sources/m4/m4/m4/m4module.h,v
retrieving revision 1.110
diff -u -p -r1.110 m4module.h
--- m4/m4module.h       7 Aug 2007 03:15:26 -0000       1.110
+++ m4/m4module.h       6 Sep 2007 22:21:45 -0000
@@ -22,7 +22,6 @@
 #ifndef M4MODULE_H
 #define M4MODULE_H 1
 
-#include <ltdl.h>
 #include <m4/hash.h>
 #include <m4/system.h>
 
@@ -37,9 +36,9 @@ typedef struct m4_builtin     m4_builtin;
 typedef struct m4_macro                m4_macro;
 typedef struct m4_symbol_value m4_symbol_value;
 typedef struct m4_input_block  m4_input_block;
+typedef struct m4_module       m4_module;
 
 typedef struct obstack         m4_obstack;
-typedef lt_dlsymlist           m4_export;
 
 typedef void   m4_builtin_func  (m4 *, m4_obstack *, int, m4_symbol_value **);
 
@@ -87,15 +86,15 @@ struct m4_macro
 
 #define M4INIT_HANDLER(name)                                   \
   void CONC(name, CONC(_LTX_, m4_init_module))                 \
-       (m4 *context, lt_dlhandle handle, m4_obstack *obs);     \
+       (m4 *context, m4_module *handle, m4_obstack *obs);      \
   void CONC(name, CONC(_LTX_, m4_init_module))                 \
-       (m4 *context, lt_dlhandle handle, m4_obstack *obs)
+       (m4 *context, m4_module *handle, m4_obstack *obs)
 
 #define M4FINISH_HANDLER(name)                                 \
   void CONC(name, CONC(_LTX_, m4_finish_module))               \
-       (m4 *context, lt_dlhandle handle, m4_obstack *obs);     \
+       (m4 *context, m4_module *handle, m4_obstack *obs);      \
   void CONC(name, CONC(_LTX_, m4_finish_module))               \
-       (m4 *context, lt_dlhandle handle, m4_obstack *obs)
+       (m4 *context, m4_module *handle, m4_obstack *obs)
 
 #define M4_MODULE_IMPORT(M, S)                                 \
   CONC(S, _func) *S = (CONC(S, _func) *)                       \
@@ -179,16 +178,18 @@ m4_context_opt_bit_table
 
 /* --- MODULE MANAGEMENT --- */
 
-typedef void m4_module_init_func   (m4 *, lt_dlhandle, m4_obstack*);
-typedef void m4_module_finish_func (m4 *, lt_dlhandle, m4_obstack*);
+typedef void m4_module_init_func   (m4 *, m4_module *, m4_obstack *);
+typedef void m4_module_finish_func (m4 *, m4_module *, m4_obstack *);
 
-extern lt_dlhandle  m4_module_load     (m4 *, const char*, m4_obstack*);
+extern m4_module *  m4_module_load     (m4 *, const char*, m4_obstack*);
+extern const char * m4_module_makeresident (m4_module *);
+extern int         m4_module_refcount (const m4_module *);
 extern void        m4_module_unload   (m4 *, const char*, m4_obstack*);
 extern void *      m4_module_import   (m4 *, const char*, const char*,
                                        m4_obstack*);
 
-extern const char * m4_get_module_name (lt_dlhandle);
-extern void        m4__module_exit    (m4 *context);
+extern const char * m4_get_module_name (const m4_module *);
+extern void        m4__module_exit    (m4 *);
 
 
 
@@ -272,8 +273,8 @@ extern void         m4_set_symbol_value_placeho
 
 /* --- BUILTIN MANAGEMENT --- */
 
-extern m4_symbol_value *m4_builtin_find_by_name (lt_dlhandle, const char *);
-extern const m4_builtin        *m4_builtin_find_by_func (lt_dlhandle,
+extern m4_symbol_value *m4_builtin_find_by_name (m4_module *, const char *);
+extern const m4_builtin        *m4_builtin_find_by_func (m4_module *,
                                                  m4_builtin_func *);
 
 
Index: m4/m4private.h
===================================================================
RCS file: /sources/m4/m4/m4/m4private.h,v
retrieving revision 1.82
diff -u -p -r1.82 m4private.h
--- m4/m4private.h      7 Aug 2007 03:15:26 -0000       1.82
+++ m4/m4private.h      6 Sep 2007 22:21:45 -0000
@@ -23,6 +23,7 @@
 #define M4PRIVATE_H 1
 
 #include <m4/m4module.h>
+#include <ltdl.h>
 
 #include "cloexec.h"
 
@@ -143,12 +144,22 @@ struct m4 {
 #define INIT_SYMBOL            "m4_init_module"
 #define FINISH_SYMBOL          "m4_finish_module"
 
+struct m4_module
+{
+  lt_dlhandle  handle;         /* ltdl module information.  */
+  /* TODO: add struct members, such as copy of builtins (so that we
+     can store additional information about builtins, and so that the
+     list isn't changed by the module behind our backs once we have
+     initialized it) or cached pointers (to reduce the number of calls
+     needed to lt_dlsym).  */
+};
+
 extern void        m4__module_init (m4 *context);
-extern lt_dlhandle  m4__module_open (m4 *context, const char *name,
+extern m4_module *  m4__module_open (m4 *context, const char *name,
                                     m4_obstack *obs);
 extern void        m4__module_exit (m4 *context);
-extern lt_dlhandle  m4__module_next (lt_dlhandle);
-extern lt_dlhandle  m4__module_find (const char *name);
+extern m4_module *  m4__module_next (m4_module *);
+extern m4_module *  m4__module_find (const char *name);
 
 
 
@@ -162,8 +173,8 @@ struct m4_symbol
 
 struct m4_symbol_value {
   m4_symbol_value *    next;
-  lt_dlhandle          handle;
-  int                  flags;
+  m4_module *          handle;
+  unsigned int         flags;
 
   m4_hash *            arg_signature;
   unsigned int         min_args;
@@ -252,7 +263,8 @@ struct m4_symbol_arg {
 #define SYMBOL_ARG_REST_BIT    (1 << 0)
 #define SYMBOL_ARG_KEY_BIT     (1 << 1)
 
-extern void m4__symtab_remove_module_references (m4_symbol_table*, 
lt_dlhandle);
+extern void m4__symtab_remove_module_references (m4_symbol_table*,
+                                                m4_module *);
 
 
 
Index: m4/module.c
===================================================================
RCS file: /sources/m4/m4/m4/module.c,v
retrieving revision 1.52
diff -u -p -r1.52 module.c
--- m4/module.c 7 Aug 2007 03:15:27 -0000       1.52
+++ m4/module.c 6 Sep 2007 22:21:45 -0000
@@ -82,13 +82,13 @@
 #define MODULE_SELF_NAME       "!myself!"
 
 static const char*  module_dlerror (void);
-static int         module_remove  (m4 *context, lt_dlhandle handle,
-                                   m4_obstack *obs);
-static void        module_close   (m4 *context, lt_dlhandle handle,
+static int         module_remove  (m4 *context, m4_module *handle,
                                    m4_obstack *obs);
+//static void      module_close   (m4 *context, m4_module *handle,
+//                                 m4_obstack *obs);
 
-static const m4_builtin * install_builtin_table (m4*, lt_dlhandle);
-static const m4_macro *   install_macro_table   (m4*, lt_dlhandle);
+static void        install_builtin_table (m4*, m4_module *);
+static void        install_macro_table   (m4*, m4_module *);
 
 static int         m4__module_interface        (lt_dlhandle handle,
                                                 const char *id_string);
@@ -96,13 +96,13 @@ static int      m4__module_interface        (lt_
 static lt_dlinterface_id iface_id = NULL;
 
 const char *
-m4_get_module_name (lt_dlhandle handle)
+m4_get_module_name (const m4_module *m4_handle)
 {
   const lt_dlinfo *info;
 
-  assert (handle);
+  assert (m4_handle && m4_handle->handle);
 
-  info = lt_dlgetinfo (handle);
+  info = lt_dlgetinfo (m4_handle->handle);
 
   return info ? info->name : 0;
 }
@@ -111,35 +111,37 @@ void *
 m4_module_import (m4 *context, const char *module_name,
                  const char *symbol_name, m4_obstack *obs)
 {
-  lt_dlhandle  handle          = m4__module_find (module_name);
-  lt_ptr       symbol_address  = NULL;
+  m4_module *  handle          = m4__module_find (module_name);
+  void *       symbol_address  = NULL;
 
   /* Try to load the module if it is not yet available (errors are
      diagnosed by m4_module_load).  */
+  /* FIXME - should this use m4__module_open instead, to avoid
+     polluting the symbol table when importing a function?  */
   if (!handle)
     handle = m4_module_load (context, module_name, obs);
 
   if (handle)
     {
-      symbol_address = lt_dlsym (handle, symbol_name);
+      symbol_address = lt_dlsym (handle->handle, symbol_name);
 
       if (!symbol_address)
        m4_error (context, 0, 0, _("cannot load symbol `%s' from module `%s'"),
                  symbol_name, module_name);
     }
 
-  return (void *) symbol_address;
+  return symbol_address;
 }
 
-static const m4_builtin *
-install_builtin_table (m4 *context, lt_dlhandle handle)
+static void
+install_builtin_table (m4 *context, m4_module *handle)
 {
   const m4_builtin *bp;
 
   assert (context);
   assert (handle);
 
-  bp = (m4_builtin *) lt_dlsym (handle, BUILTIN_SYMBOL);
+  bp = (m4_builtin *) lt_dlsym (handle->handle, BUILTIN_SYMBOL);
   if (bp)
     {
       for (; bp->name != NULL; bp++)
@@ -175,19 +177,17 @@ install_builtin_table (m4 *context, lt_d
                        _("module %s: builtins loaded"),
                        m4_get_module_name (handle));
     }
-
-  return bp;
 }
 
-static const m4_macro *
-install_macro_table (m4 *context, lt_dlhandle handle)
+static void
+install_macro_table (m4 *context, m4_module *handle)
 {
   const m4_macro *mp;
 
   assert (context);
   assert (handle);
 
-  mp = (const m4_macro *) lt_dlsym (handle, MACRO_SYMBOL);
+  mp = (const m4_macro *) lt_dlsym (handle->handle, MACRO_SYMBOL);
 
   if (mp)
     {
@@ -205,18 +205,16 @@ install_macro_table (m4 *context, lt_dlh
                        _("module %s: macros loaded"),
                        m4_get_module_name (handle));
     }
-
-  return mp;
 }
 
-lt_dlhandle
+m4_module *
 m4_module_load (m4 *context, const char *name, m4_obstack *obs)
 {
-  const lt_dlhandle handle = m4__module_open (context, name, obs);
+  m4_module *handle = m4__module_open (context, name, obs);
 
   if (handle)
     {
-      const lt_dlinfo  *info   = lt_dlgetinfo (handle);
+      const lt_dlinfo  *info   = lt_dlgetinfo (handle->handle);
 
       if (!info)
        {
@@ -239,11 +237,36 @@ m4_module_load (m4 *context, const char 
   return handle;
 }
 
+/* Make the module HANDLE resident.  Return NULL on success, or a
+   pre-translated error string on failure.  */
+const char *
+m4_module_makeresident (m4_module *m4_handle)
+{
+  assert (m4_handle);
+  return lt_dlmakeresident (m4_handle->handle) ? module_dlerror () : NULL;
+}
+
+/* Return the current refcount, or times that module HANDLE has been
+   opened.  */
+int
+m4_module_refcount (const m4_module *handle)
+{
+  /* FIXME - we should track refcounts in struct m4_module, since it
+     is conceivable that the m4 load refcount could be different than
+     the libltdl refcount, if a single module can be loaded both by
+     m4_module_load and as a dependent of some other module.  */
+  const lt_dlinfo *info;
+  assert (handle);
+  info = lt_dlgetinfo (handle->handle);
+  assert (info);
+  return info->ref_count;
+}
+
 /* Unload a module.  */
 void
 m4_module_unload (m4 *context, const char *name, m4_obstack *obs)
 {
-  lt_dlhandle  handle  = NULL;
+  m4_module *  handle  = NULL;
   int          errors  = 0;
 
   assert (context);
@@ -284,22 +307,53 @@ m4__module_interface (lt_dlhandle handle
 
 /* Return successive loaded modules that pass the interface test registered
    with the interface id.  */
-lt_dlhandle
-m4__module_next (lt_dlhandle handle)
+m4_module *
+m4__module_next (m4_module *m4_handle)
 {
+  lt_dlhandle handle = m4_handle ? m4_handle->handle : NULL;
   assert (iface_id);
 
-  return lt_dlhandle_iterate (iface_id, handle);
+  /* Resident modules still show up in the lt_dlhandle_iterate loop
+     after they have been unloaded from m4.  */
+  do
+    {
+      handle = lt_dlhandle_iterate (iface_id, handle);
+      if (!handle)
+       return NULL;
+      m4_handle = (m4_module *) lt_dlcaller_get_data (iface_id, handle);
+      if (m4_handle)
+       assert (m4_handle->handle == handle);
+      else
+       {
+         assert (lt_dlisresident (handle));
+         assert (lt_dlgetinfo (handle)->ref_count <= 1);
+       }
+    }
+  while (!m4_handle);
+  return m4_handle;
 }
 
 /* Return the first loaded module that passes the registered interface test
    and is called NAME.  */
-lt_dlhandle
+m4_module *
 m4__module_find (const char *name)
 {
+  lt_dlhandle handle;
+  m4_module *m4_handle;
   assert (iface_id);
 
-  return lt_dlhandle_fetch (iface_id, name);
+  handle = lt_dlhandle_fetch (iface_id, name);
+  if (!handle)
+    return NULL;
+  m4_handle = (m4_module *) lt_dlcaller_get_data (iface_id, handle);
+  if (m4_handle)
+    assert (m4_handle->handle == handle);
+  else
+    {
+      assert (lt_dlisresident (handle));
+      assert (lt_dlgetinfo (handle)->ref_count <= 1);
+    }
+  return m4_handle;
 }
 
 
@@ -369,10 +423,11 @@ m4__module_init (m4 *context)
 /* Load a module.  NAME can be a absolute file name or, if relative,
    it is searched for in the module path.  The module is unloaded in
    case of error.  */
-lt_dlhandle
+m4_module *
 m4__module_open (m4 *context, const char *name, m4_obstack *obs)
 {
   lt_dlhandle          handle          = lt_dlopenext (name);
+  m4_module *          m4_handle       = NULL;
   m4_module_init_func *        init_func       = NULL;
 
   assert (context);
@@ -389,21 +444,43 @@ m4__module_open (m4 *context, const char
                        _("module %s: opening file `%s'"),
                        name ? name : MODULE_SELF_NAME, info->filename);
 
+      /* Provide the m4_module correpsonding to the lt_dlhandle, if
+        not yet created.  */
+      m4_handle = (m4_module *) lt_dlcaller_get_data (iface_id, handle);
+      if (!m4_handle)
+       {
+         void *old;
+         const char *err;
+
+         assert (info->ref_count == 1);
+         m4_handle = (m4_module *) xzalloc (sizeof *m4_handle);
+         m4_handle->handle = handle;
+
+         /* clear out any stale errors, since we have to use
+            lt_dlerror to distinguish between success and
+            failure.  */
+         lt_dlerror ();
+         old = lt_dlcaller_set_data (iface_id, handle, m4_handle);
+         assert (!old);
+         err = lt_dlerror ();
+         if (err)
+           m4_error (context, EXIT_FAILURE, 0,
+                     _("unable to load module `%s': %s"), name, err);
+       }
+
       /* Find and run any initializing function in the opened module,
         each time the module is opened.  */
       init_func = (m4_module_init_func *) lt_dlsym (handle, INIT_SYMBOL);
       if (init_func)
        {
-         (*init_func) (context, handle, obs);
+         init_func (context, m4_handle, obs);
 
          m4_debug_message (context, M4_DEBUG_TRACE_MODULE,
                            _("module %s: init hook called"), name);
        }
-
-      if (!init_func
-         && !lt_dlsym (handle, FINISH_SYMBOL)
-         && !lt_dlsym (handle, BUILTIN_SYMBOL)
-         && !lt_dlsym (handle, MACRO_SYMBOL))
+      else if (!lt_dlsym (handle, FINISH_SYMBOL)
+              && !lt_dlsym (handle, BUILTIN_SYMBOL)
+              && !lt_dlsym (handle, MACRO_SYMBOL))
        {
          m4_error (context, EXIT_FAILURE, 0,
                    _("module `%s' has no entry points"), name);
@@ -419,19 +496,19 @@ m4__module_open (m4 *context, const char
                name, module_dlerror ());
     }
 
-  return handle;
+  return m4_handle;
 }
 
 void
 m4__module_exit (m4 *context)
 {
-  lt_dlhandle  handle  = m4__module_next (0);
+  m4_module *  handle  = m4__module_next (NULL);
   int          errors  = 0;
 
   while (handle && !errors)
     {
-      const lt_dlinfo *info    = lt_dlgetinfo (handle);
-      lt_dlhandle      pending = handle;
+      const lt_dlinfo *info    = lt_dlgetinfo (handle->handle);
+      m4_module *      pending = handle;
 
       /* If we are about to unload the final reference, move on to the
         next handle before we unload the current one.  */
@@ -457,6 +534,7 @@ m4__module_exit (m4 *context)
 
 
 
+/* FIXME - libtool doesn't expose lt_dlerror strings for translation.  */
 static const char *
 module_dlerror (void)
 {
@@ -468,66 +546,30 @@ module_dlerror (void)
   return dlerror;
 }
 
-static void
-module_close (m4 *context, lt_dlhandle handle, m4_obstack *obs)
-{
-  m4_module_finish_func *finish_func;
-  const char           *name;
-  int                   errors         = 0;
-
-  assert (handle);
-
-  /* Be careful when closing myself.  */
-  name = m4_get_module_name (handle);
-  name = xstrdup (name ? name : MODULE_SELF_NAME);
-
-  /* Run any finishing function for the opened module. */
-  finish_func = (m4_module_finish_func *) lt_dlsym (handle, FINISH_SYMBOL);
-
-  if (finish_func)
-    {
-      (*finish_func) (context, handle, obs);
-
-      m4_debug_message (context, M4_DEBUG_TRACE_MODULE,
-                       _("module %s: finish hook called"), name);
-    }
-
-  if (!lt_dlisresident (handle))
-    {
-      errors = lt_dlclose (handle);
-      if (!errors)
-       {
-         m4_debug_message (context, M4_DEBUG_TRACE_MODULE,
-                           _("module %s: closed"), name);
-       }
-    }
-  else
-    m4_debug_message (context, M4_DEBUG_TRACE_MODULE,
-                     _("module %s: resident module not closed"), name);
-
-  if (errors)
-    {
-      m4_error (context, EXIT_FAILURE, 0, _("cannot close module `%s': %s"),
-               name, module_dlerror ());
-    }
-
-  DELETE (name);
-}
-
+/* Close one reference to the module M4_HANDLE, and output to OBS any
+   information from the finish hook of the module.  If no references
+   to M4_HANDLE remain, also remove all symbols and other memory
+   associated with the module.  */
 static int
-module_remove (m4 *context, lt_dlhandle handle, m4_obstack *obs)
+module_remove (m4 *context, m4_module *m4_handle, m4_obstack *obs)
 {
-  const lt_dlinfo *info;
-  int             errors       = 0;
-  const char *    name;
+  const lt_dlinfo *            info;
+  int                          errors  = 0;
+  const char *                 name;
+  lt_dlhandle                  handle;
+  bool                         last_reference = false;
+  bool                         resident = false;
+  m4_module_finish_func *      finish_func;
 
-  assert (handle);
+  assert (m4_handle && m4_handle->handle);
 
   /* Be careful when closing myself.  */
-  name = m4_get_module_name (handle);
+  handle = m4_handle->handle;
+  name = m4_get_module_name (m4_handle);
   name = xstrdup (name ? name : MODULE_SELF_NAME);
 
   info = lt_dlgetinfo (handle);
+  resident = info->is_resident;
 
   /* Only do the actual close when the number of calls to close this
      module is equal to the number of times it was opened. */
@@ -535,7 +577,7 @@ module_remove (m4 *context, lt_dlhandle 
   if (info->ref_count > 1)
     {
       fprintf (stderr, "module %s: now has %d references.",
-              name, info->ref_count -1);
+              name, info->ref_count - 1);
     }
 #endif /* DEBUG_MODULES */
 
@@ -545,14 +587,58 @@ module_remove (m4 *context, lt_dlhandle 
         equal to 1.  If module_close is called again on a
         resident module after the references have already been
         removed, we needn't try to remove them again!  */
-      m4__symtab_remove_module_references (M4SYMTAB, handle);
+      m4__symtab_remove_module_references (M4SYMTAB, m4_handle);
 
       m4_debug_message (context, M4_DEBUG_TRACE_MODULE,
                        _("module %s: symbols unloaded"), name);
+      last_reference = true;
     }
 
-  if (!errors)
-    module_close (context, handle, obs);
+  finish_func = (m4_module_finish_func *) lt_dlsym (handle, FINISH_SYMBOL);
+  if (finish_func)
+    {
+      finish_func (context, m4_handle, obs);
+
+      m4_debug_message (context, M4_DEBUG_TRACE_MODULE,
+                       _("module %s: finish hook called"), name);
+    }
+
+  if (last_reference && resident)
+    {
+      /* Special case when closing last reference to resident module -
+        we need to remove the association of the m4_module wrapper
+        with the dlhandle, because we are about to free the wrapper,
+        but the module will still show up in lt_dlhandle_iterate.
+        Still call lt_dlclose to reduce the ref count, but ignore the
+        failure about not closing a resident module.  */
+      void *old = lt_dlcaller_set_data (iface_id, handle, NULL);
+      if (!old)
+       m4_error (context, EXIT_FAILURE, 0,
+                 _("unable to close module `%s': %s"), name,
+                 module_dlerror());
+      assert (old == m4_handle);
+      lt_dlclose (handle);
+      m4_debug_message (context, M4_DEBUG_TRACE_MODULE,
+                       _("module %s: resident module not closed"), name);
+    }
+  else
+    {
+      errors = lt_dlclose (handle);
+      /* Ignore the error expected if the module was resident.  */
+      if (resident)
+       errors = 0;
+      if (!errors)
+       {
+         m4_debug_message (context, M4_DEBUG_TRACE_MODULE,
+                           _("module %s: closed"), name);
+       }
+    }
+
+  if (errors)
+    m4_error (context, EXIT_FAILURE, 0, _("cannot close module `%s': %s"),
+             name, module_dlerror ());
+  if (last_reference)
+    free (m4_handle);
 
   DELETE (name);
 
Index: m4/symtab.c
===================================================================
RCS file: /sources/m4/m4/m4/symtab.c,v
retrieving revision 1.74
diff -u -p -r1.74 symtab.c
--- m4/symtab.c 7 Aug 2007 03:15:27 -0000       1.74
+++ m4/symtab.c 6 Sep 2007 22:21:45 -0000
@@ -143,7 +143,7 @@ symtab_fetch (m4_symbol_table *symtab, c
    the symbol table.  */
 void
 m4__symtab_remove_module_references (m4_symbol_table *symtab,
-                                    lt_dlhandle handle)
+                                    m4_module *handle)
 {
   m4_hash_iterator *place = 0;
 
Index: modules/load.c
===================================================================
RCS file: /sources/m4/m4/modules/load.c,v
retrieving revision 1.24
diff -u -p -r1.24 load.c
--- modules/load.c      7 Aug 2007 03:15:29 -0000       1.24
+++ modules/load.c      6 Sep 2007 22:21:45 -0000
@@ -75,9 +75,10 @@ m4_macro m4_macro_table[] =
    unload is in progress.  */
 M4INIT_HANDLER (load)
 {
-  if (handle && lt_dlmakeresident (handle) != 0)
+  const char *err = m4_module_makeresident (handle);
+  if (err)
     m4_error (context, 0, 0, _("cannot make module `%s' resident: %s"),
-             m4_get_module_name (handle), lt_dlerror ());
+             m4_get_module_name (handle), err);
 }
 
 
Index: modules/m4.c
===================================================================
RCS file: /sources/m4/m4/modules/m4.c,v
retrieving revision 1.112
diff -u -p -r1.112 m4.c
--- modules/m4.c        7 Aug 2007 03:15:29 -0000       1.112
+++ modules/m4.c        6 Sep 2007 22:21:45 -0000
@@ -39,7 +39,6 @@
 #include <modules/m4.h>
 
 /* Rename exported symbols for dlpreload()ing.  */
-#define m4_export_table                m4_LTX_m4_export_table
 #define m4_builtin_table       m4_LTX_m4_builtin_table
 
 #define m4_set_sysval          m4_LTX_m4_set_sysval
@@ -141,11 +140,10 @@ m4_builtin m4_builtin_table[] =
    progress.  */
 M4INIT_HANDLER (m4)
 {
-  if (handle && lt_dlmakeresident (handle) != 0)
-    {
-      m4_error (context, 0, 0, _("cannot make module `%s' resident: %s"),
-               m4_get_module_name (handle), lt_dlerror ());
-    }
+  const char *err = m4_module_makeresident (handle);
+  if (err)
+    m4_error (context, 0, 0, _("cannot make module `%s' resident: %s"),
+             m4_get_module_name (handle), err);
 }
 
 
Index: modules/shadow.c
===================================================================
RCS file: /sources/m4/m4/modules/shadow.c,v
retrieving revision 1.17
diff -u -p -r1.17 shadow.c
--- modules/shadow.c    7 Aug 2007 03:15:29 -0000       1.17
+++ modules/shadow.c    6 Sep 2007 22:21:45 -0000
@@ -67,14 +67,11 @@ m4_macro m4_macro_table[] =
 
 M4INIT_HANDLER (shadow)
 {
-  const lt_dlinfo *info = 0;
   const char *s = "Shadow module loaded.";
-
-  if (handle)
-    info = lt_dlgetinfo (handle);
+  int refcount = m4_module_refcount (handle);
 
   /* Only display the message on first load.  */
-  if (obs && info && (info->ref_count == 1))
+  if (obs && refcount == 1)
     obstack_grow (obs, s, strlen (s));
 }
 
Index: src/freeze.c
===================================================================
RCS file: /sources/m4/m4/src/freeze.c,v
retrieving revision 1.68
diff -u -p -r1.68 freeze.c
--- src/freeze.c        7 Aug 2007 03:15:30 -0000       1.68
+++ src/freeze.c        6 Sep 2007 22:21:45 -0000
@@ -263,7 +263,7 @@ produce_frozen_state (m4 *context, const
     produce_syntax_dump (file, M4SYNTAX, *str++);
 
   /* Dump all loaded modules.  */
-  produce_module_dump (file, m4__module_next (0));
+  produce_module_dump (file, m4__module_next (NULL));
 
   /* Dump all symbols.  */
   produce_symbol_dump (context, file, M4SYMTAB);
Index: src/main.c
===================================================================
RCS file: /sources/m4/m4/src/main.c,v
retrieving revision 1.116
diff -u -p -r1.116 main.c
--- src/main.c  11 Aug 2007 15:50:59 -0000      1.116
+++ src/main.c  6 Sep 2007 22:21:45 -0000
@@ -623,11 +623,11 @@ main (int argc, char *const *argv, char 
     reload_frozen_state (context, frozen_file_to_read);
   else
     {
-      m4_module_load (context, "m4", 0);
+      m4_module_load (context, "m4", NULL);
       if (m4_get_posixly_correct_opt (context))
-       m4_module_load (context, "traditional", 0);
+       m4_module_load (context, "traditional", NULL);
       else
-       m4_module_load (context, "gnu", 0);
+       m4_module_load (context, "gnu", NULL);
     }
 
   /* Import environment variables as macros.  The definition are
Index: tests/modules.at
===================================================================
RCS file: /sources/m4/m4/tests/modules.at,v
retrieving revision 1.26
diff -u -p -r1.26 modules.at
--- tests/modules.at    7 Aug 2007 03:15:32 -0000       1.26
+++ tests/modules.at    6 Sep 2007 22:21:45 -0000
@@ -487,3 +487,45 @@ forloop(`i', `1', `5000', `unload(`gnu')
 AT_CHECK_M4([-m load input.m4], [0])
 
 AT_CLEANUP
+
+
+## ------------------ ##
+## unload load module ##
+## ------------------ ##
+
+AT_SETUP([unload load module])
+AT_CHECK_DYNAMIC_MODULE
+
+dnl Ensure that the load module can be unloaded and reloaded (obviously,
+dnl it can't reload itself; the reload occurs on the command line).  Since
+dnl the module must be resident (after all, the `unload' builtin had better
+dnl not unmap the memory for the code it is currently executing), make sure
+dnl that resident modules are handled gracefully.
+AT_DATA([in1.m4], [[__load__ 1
+unload(`load') 2
+__load__ 3
+]])
+
+AT_DATA([in2.m4], [[__load__ 4
+load(`load') 5
+__load__ 6
+unload(`load') 7
+__load__ 8
+unload(`load') 9
+__load__ 10
+]])
+
+AT_CHECK_M4([-m load in1.m4 -m load in2.m4], [0],
+[[ 1
+ 2
+__load__ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+__load__ 10
+]])
+
+AT_CLEANUP






reply via email to

[Prev in Thread] Current Thread [Next in Thread]