[Top][All Lists]
[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
Re: module cleanup [1/n], Gary V. Vaughan, 2007/09/11