Please take our Survey
logo       

Choosing A Webhost:
A web hosting service is a type of Internet hosting service that allows individuals and organizations to provide their own website accessible via the World Wide Web. Web hosts are companies that provide space on a server they own for use by their clients as well as providing Internet connectivity, typically in a data center. Web hosts can also provide data center space and connectivity to the Internet for servers they do not own to be located in their data center, called colocation. more...

[Patch mp2] Solving module reloading problems (Apache::Reload) with ModPerl: msg#00389

apache.mod-perl.devel

Subject: [Patch mp2] Solving module reloading problems (Apache::Reload) with ModPerl::Util::clear_namespace()

I have been trying to take care of the problem Apache::Reload is having with
clearing certain types of subroutines. This problem also exist in at least
2 other places, namely ModPerl::PerlRun and <Perl> sections, that both also
need to be able to destroy a namespace (a module, really).

It occured to me that there was an alternative approach to trying to manually
delete each entry in a package's namespace. If you want to destroy a loaded
module,
say Foo::Bar::Baz, you can try to iterate over it's stash and destroy all
that's in
there, our currrent approach.

Or you can (simply put) "delete $Foo::Bar::{'Baz::'};". Deleting the entire
stash
from that package. This is has many advantages. 1. If other modules are holding
references to internal constructs of that package, thru reference couting, they
will still hold _valid_ references to whatever they are currently pointing, a
good
thing. 2. General cleanup will happen in that module, so package globals with
DESTROY methods will be called, etc.

All together, it's a pretty clean approach, with the only downside that
'unloading'
a module that way might not entirely flush the memory it's using (if things are
kept
alive thru references).

I've attached a patch that does this. It adds a ModPerl::Util::clear_namespace()
and it's used in the 3 places that I could see need it, PerlSections,
ModPerl::PerlRun
and Apache::Reload.

Thoughts ?

P.S. Documentation patch not yet complete, I am working on it too.
--
--------------------------------------------------------------------------------
Philippe M. Chiasson m/gozer\@(apache|cpan|ectoplasm)\.org/ GPG KeyID : 88C3A5A5
http://gozer.ectoplasm.org/ F9BF E0C2 480E 7680 1AE5 3631 CB32 A107 88C3A5A5
Index: ModPerl-Registry/lib/ModPerl/RegistryCooker.pm
===================================================================
RCS file: /home/cvs/modperl-2.0/ModPerl-Registry/lib/ModPerl/RegistryCooker.pm,v
retrieving revision 1.50
diff -u -I$Id -r1.50 RegistryCooker.pm
--- ModPerl-Registry/lib/ModPerl/RegistryCooker.pm 27 Jun 2004 21:26:45
-0000 1.50
+++ ModPerl-Registry/lib/ModPerl/RegistryCooker.pm 24 Aug 2004 17:52:01
-0000
@@ -526,47 +526,7 @@

$self->debug("flushing namespace") if DEBUG & D_NOISE;

- no strict 'refs';
- my $tab = \%{ $self->{PACKAGE} . '::' };
-
- # below we assign to a symbol first before undef'ing it, to avoid
- # nuking aliases. If we undef directly we may undef not only the
- # alias but the original function as well
-
- for (keys %$tab) {
- my $fullname = join '::', $self->{PACKAGE}, $_;
- # code/hash/array/scalar might be imported make sure the gv
- # does not point elsewhere before undefing each
- if (%$fullname) {
- *{$fullname} = {};
- undef %$fullname;
- }
- if (@$fullname) {
- *{$fullname} = [];
- undef @$fullname;
- }
- if ($$fullname) {
- my $tmp; # argh, no such thing as an anonymous scalar
- *{$fullname} = \$tmp;
- undef $$fullname;
- }
- if (defined &$fullname) {
- no warnings;
- local $^W = 0;
- if (defined(my $p = prototype $fullname)) {
- *{$fullname} = eval "sub ($p) {}";
- }
- else {
- *{$fullname} = sub {};
- }
- undef &$fullname;
- }
- if (*{$fullname}{IO}) {
- if (fileno $fullname) {
- close $fullname;
- }
- }
- }
+ ModPerl::Util::clear_namespace($self->{REQ}->pool, $self->{PACKAGE});
}


Index: lib/Apache/Reload.pm
===================================================================
RCS file: /home/cvs/modperl-2.0/lib/Apache/Reload.pm,v
retrieving revision 1.14
diff -u -I$Id -r1.14 Reload.pm
--- lib/Apache/Reload.pm 11 Mar 2004 06:34:24 -0000 1.14
+++ lib/Apache/Reload.pm 24 Aug 2004 17:52:01 -0000
@@ -27,6 +27,8 @@
use Apache::ServerUtil;
use Apache::RequestUtil;

+use ModPerl::Util;
+
use vars qw(%INCS %Stat $TouchTime %UndefFields);

%Stat = ($INC{"Apache/Reload.pm"} => time);
@@ -47,6 +49,13 @@
return $package;
}

+sub module_to_package {
+ my $module = shift;
+ $module =~ s/\//::/g;
+ $module =~ s/\.pm$//g;
+ return $module;
+}
+
sub register_module {
my($class, $package, $file) = @_;
my $module = package_to_module($package);
@@ -59,11 +68,6 @@
return unless $file;
$INCS{$module} = $file;
}
-
- no strict 'refs';
- if (%{"${package}::FIELDS"}) {
- $UndefFields{$module} = "${package}::FIELDS";
- }
}

# the first argument is:
@@ -110,15 +114,6 @@
foreach my $match (keys %INC) {
if ($match =~ /^\Q$prefix\E/) {
$Apache::Reload::INCS{$match} = $INC{$match};
- my $package = $match;
- $package =~ s/\//::/g;
- $package =~ s/\.pm$//;
- no strict 'refs';
-# warn "checking for FIELDS on $package\n";
- if (%{"${package}::FIELDS"}) {
-# warn "found fields in $package\n";
- $UndefFields{$match} = "${package}::FIELDS";
- }
}
}
}
@@ -152,29 +147,16 @@
}

if ($mtime > $Stat{$file}) {
- delete $INC{$key};
-# warn "Reloading $key\n";
- if (my $symref = $UndefFields{$key}) {
-# warn "undeffing fields\n";
- no strict 'refs';
- undef %{$symref};
- }
- no warnings FATAL => 'all';
- local $SIG{__WARN__} = \&skip_redefine_const_sub_warn
- unless $ConstantRedefineWarnings;
+ my $package = module_to_package($key);
+ ModPerl::Util::clear_namespace($o->pool, $package);
require $key;
- warn("Apache::Reload: process $$ reloading $key\n")
+ warn("Apache::Reload: process $$ reloading $package from $key\n")
if $DEBUG;
}
$Stat{$file} = $mtime;
}

return Apache::OK;
-}
-
-sub skip_redefine_const_sub_warn {
- return if $_[0] =~ /^Constant subroutine [\w:]+ redefined at/;
- CORE::warn(@_);
}

1;
Index: src/modules/perl/modperl_cmd.c
===================================================================
RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_cmd.c,v
retrieving revision 1.64
diff -u -I$Id -r1.64 modperl_cmd.c
--- src/modules/perl/modperl_cmd.c 23 Aug 2004 21:16:27 -0000 1.64
+++ src/modules/perl/modperl_cmd.c 24 Aug 2004 17:52:01 -0000
@@ -577,10 +577,7 @@
SvREFCNT_dec((SV*)args);

if (!(saveconfig && SvTRUE(saveconfig))) {
- HV *symtab = (HV*)gv_stashpv(pkg_name, FALSE);
- if (symtab) {
- modperl_clear_symtab(aTHX_ symtab);
- }
+ modperl_clear_stash(aTHX_ p, pkg_name);
}

if (status != OK) {
Index: src/modules/perl/modperl_mgv.c
===================================================================
RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_mgv.c,v
retrieving revision 1.35
diff -u -I$Id -r1.35 modperl_mgv.c
--- src/modules/perl/modperl_mgv.c 4 Mar 2004 06:01:07 -0000 1.35
+++ src/modules/perl/modperl_mgv.c 24 Aug 2004 17:52:01 -0000
@@ -171,32 +171,6 @@
}
#endif

-
-static void package2filename(apr_pool_t *p, const char *package,
- char **filename, int *len)
-{
- const char *s;
- char *d;
-
- *filename = apr_palloc(p, (strlen(package)+4)*sizeof(char));
-
- for (s = package, d = *filename; *s; s++, d++) {
- if (*s == ':' && s[1] == ':') {
- *d = '/';
- s++;
- }
- else {
- *d = *s;
- }
- }
- *d++ = '.';
- *d++ = 'p';
- *d++ = 'm';
- *d = '\0';
-
- *len = d - *filename;
-}
-
/* currently used for complex filters attributes parsing */
/* XXX: may want to generalize it for any handlers */
#define MODPERL_MGV_DEEP_RESOLVE(handler, p) \
@@ -285,7 +259,7 @@
char *filename;
SV **svp;

- package2filename(p, name, &filename, &len);
+ modperl_package2filename(p, name, &filename, &len);
svp = hv_fetch(GvHVn(PL_incgv), filename, len, 0);

if (!(svp && *svp != &PL_sv_undef)) { /* not in %INC */
Index: src/modules/perl/modperl_util.c
===================================================================
RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_util.c,v
retrieving revision 1.76
diff -u -I$Id -r1.76 modperl_util.c
--- src/modules/perl/modperl_util.c 22 Aug 2004 20:47:37 -0000 1.76
+++ src/modules/perl/modperl_util.c 24 Aug 2004 17:52:01 -0000
@@ -491,60 +491,6 @@
return (*name && gv_stashpv(name, FALSE)) ? 1 : 0;
}

-static int modperl_gvhv_is_stash(GV *gv)
-{
- int len = GvNAMELEN(gv);
- char *name = GvNAME(gv);
-
- if ((len > 2) && (name[len - 1] == ':') && (name[len - 2] == ':')) {
- return 1;
- }
-
- return 0;
-}
-
-/*
- * we do not clear symbols within packages, the desired behavior
- * for directive handler classes. and there should never be a package
- * within the %Apache::ReadConfig. nothing else that i'm aware of calls
- * this function, so we should be ok.
- */
-
-void modperl_clear_symtab(pTHX_ HV *symtab)
-{
- SV *val;
- char *key;
- I32 klen;
-
- hv_iterinit(symtab);
-
- while ((val = hv_iternextsv(symtab, &key, &klen))) {
- SV *sv;
- HV *hv;
- AV *av;
- CV *cv;
-
- if ((SvTYPE(val) != SVt_PVGV) || GvIMPORTED((GV*)val)) {
- continue;
- }
- if ((sv = GvSV((GV*)val))) {
- sv_setsv(GvSV((GV*)val), &PL_sv_undef);
- }
- if ((hv = GvHV((GV*)val)) && !modperl_gvhv_is_stash((GV*)val)) {
- hv_clear(hv);
- }
- if ((av = GvAV((GV*)val))) {
- av_clear(av);
- }
- if ((cv = GvCV((GV*)val)) && (GvSTASH((GV*)val) == GvSTASH(CvGV(cv))))
{
- GV *gv = CvGV(cv);
- cv_undef(cv);
- CvGV(cv) = gv;
- GvCVGEN(gv) = 1; /* invalidate method cache */
- }
- }
-}
-
#define SLURP_SUCCESS(action) \
if (rc != APR_SUCCESS) { \
SvREFCNT_dec(sv); \
@@ -749,4 +695,95 @@
}

return array;
+}
+
+void modperl_package2filename(apr_pool_t *p, const char *package,
+ char **filename, int *len)
+{
+ const char *s;
+ char *d;
+

+ *filename = apr_palloc(p, (strlen(package)+4)*sizeof(char));
+

+ for (s = package, d = *filename; *s; s++, d++) {
+ if (*s == ':' && s[1] == ':') {
+ *d = '/';
+ s++;
+ }
+ else {
+ *d = *s;
+ }
+ }
+ *d++ = '.';
+ *d++ = 'p';
+ *d++ = 'm';
+ *d = '\0';
+

+ *len = d - *filename;
+}
+
+void modperl_clear_stash(pTHX_ apr_pool_t *p, const char *package)
+{
+ char const *start_colon, *end_colon;
+ char const *c;
+ char *parent, *child;
+ int size;
+ HV *stash;
+
+ /* Short-circuit out if the package doesn't exist */
+ if (!modperl_perl_module_loaded(aTHX_ package)) {
+ return;
+ }
+
+ /* Split the package name on the last '::' */
+ /* Foo::Bar::Baz */
+ c = start_colon = end_colon = package;
+
+ while (*c) {
+ if (*c == ':') {
+ start_colon = c - 1;
+ end_colon = c + 1;
+ }
+ c++;
+ }
+
+ /* parent = Foo::Bar */
+ size = (start_colon - package) + 1;
+ parent = apr_palloc(p, size);
+ apr_cpystrn(parent, package, size);
+
+ /* child = Baz:: */
+ size = strlen(package) - (end_colon - package) + 2 + 1;
+ child = apr_palloc(p, size);
+ apr_snprintf(child, size, "%s::", end_colon);
+
+ /* delete the child entry in the parent stash */
+ if ((stash = gv_stashpv(parent, FALSE))) {
+ MP_TRACE_h(MP_FUNC, "Deleting package %s with delete $%s::{%s}",
+ package, parent, child);
+ hv_delete(stash, child, size-1, G_DISCARD);
+ }
+}
+
+static void modperl_delete_from_inc(pTHX_ apr_pool_t *p,
+ const char *package)
+{
+ int len;
+ char *filename;
+
+ modperl_package2filename(p, package, &filename, &len);
+ hv_delete(GvHVn(PL_incgv), filename, len, G_DISCARD);
+
+ return;
+}
+
+void modperl_clear_namespace(pTHX_ apr_pool_t *p, const char *package)
+{
+ /* delete $INC{'Some/Package.pm} */
+ modperl_delete_from_inc(aTHX_ p, package);
+
+ /* delete $Some::{'Package::'}; */
+ modperl_clear_stash(aTHX_ p, package);
+
+ return;
}
Index: src/modules/perl/modperl_util.h
===================================================================
RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_util.h,v
retrieving revision 1.66
diff -u -I$Id -r1.66 modperl_util.h
--- src/modules/perl/modperl_util.h 22 Aug 2004 20:47:37 -0000 1.66
+++ src/modules/perl/modperl_util.h 24 Aug 2004 17:52:01 -0000
@@ -94,8 +94,6 @@
*/
SV *modperl_slurp_filename(pTHX_ request_rec *r, int tainted);

-void modperl_clear_symtab(pTHX_ HV *symtab);
-
char *modperl_file2package(apr_pool_t *p, const char *file);

/**
@@ -105,6 +103,11 @@
* @return string of original source code
*/
char *modperl_coderef2text(pTHX_ apr_pool_t *p, CV *cv);
+
+void modperl_clear_namespace(pTHX_ apr_pool_t *p, const char *package);
+void modperl_clear_stash(pTHX_ apr_pool_t *p, const char *package);
+void modperl_package2filename(apr_pool_t *p, const char *package,
+ char **filename, int *len);

SV *modperl_apr_array_header2avrv(pTHX_ apr_array_header_t *array);
apr_array_header_t *modperl_avrv2apr_array_header(pTHX_ apr_pool_t *p,
Index: t/response/TestModules/reload.pm
===================================================================
RCS file: /home/cvs/modperl-2.0/t/response/TestModules/reload.pm,v
retrieving revision 1.1
diff -u -I$Id -r1.1 reload.pm
--- t/response/TestModules/reload.pm 24 Aug 2004 17:36:56 -0000 1.1
+++ t/response/TestModules/reload.pm 24 Aug 2004 17:52:01 -0000
@@ -21,5 +21,4 @@
PerlModule Apache::Reload
PerlInitHandler Apache::TestHandler::same_interp_fixup Apache::Reload
PerlSetVar ReloadDebug On
-PerlSetVar ReloadConstantRedefineWarnings Off
PerlSetVar ReloadAll Off
Index: xs/ModPerl/Util/ModPerl__Util.h
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/ModPerl/Util/ModPerl__Util.h,v
retrieving revision 1.5
diff -u -I$Id -r1.5 ModPerl__Util.h
--- xs/ModPerl/Util/ModPerl__Util.h 4 Mar 2004 06:01:14 -0000 1.5
+++ xs/ModPerl/Util/ModPerl__Util.h 24 Aug 2004 17:52:01 -0000
@@ -28,5 +28,5 @@

#define mpxs_Apache_current_callback modperl_callback_current_callback_get

-
+#define mpxs_ModPerl__Util_clear_namespace(p, pkg)
modperl_clear_namespace(aTHX_ p, pkg)

Index: xs/maps/modperl_functions.map
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/maps/modperl_functions.map,v
retrieving revision 1.84
diff -u -I$Id -r1.84 modperl_functions.map
--- xs/maps/modperl_functions.map 22 Aug 2004 20:47:37 -0000 1.84
+++ xs/maps/modperl_functions.map 24 Aug 2004 17:52:01 -0000
@@ -5,6 +5,7 @@

MODULE=ModPerl::Util
mpxs_ModPerl__Util_untaint | | ...
+ DEFINE_clear_namespace | | apr_pool_t *:p, const char *:pkg
DEFINE_exit | | int:status=0

PACKAGE=Apache
Index: xs/tables/current/ModPerl/FunctionTable.pm
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/tables/current/ModPerl/FunctionTable.pm,v
retrieving revision 1.175
diff -u -I$Id -r1.175 FunctionTable.pm
--- xs/tables/current/ModPerl/FunctionTable.pm 22 Aug 2004 20:47:37 -0000
1.175
+++ xs/tables/current/ModPerl/FunctionTable.pm 24 Aug 2004 17:52:01 -0000
@@ -2,7 +2,7 @@

# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# ! WARNING: generated by ModPerl::ParseSource/0.01
-# ! Fri Aug 20 12:01:12 2004
+# ! Tue Aug 24 00:11:10 2004
# ! do NOT edit, any changes will be lost !
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

@@ -360,15 +360,37 @@
},
{
'return_type' => 'void',
- 'name' => 'modperl_clear_symtab',
+ 'name' => 'modperl_clear_namespace',
'args' => [
{
'type' => 'PerlInterpreter *',
'name' => 'my_perl'
},
{
- 'type' => 'HV *',
- 'name' => 'symtab'
+ 'type' => 'apr_pool_t *',
+ 'name' => 'p'
+ },
+ {
+ 'type' => 'const char *',
+ 'name' => 'package'
+ }
+ ]
+ },
+ {
+ 'return_type' => 'void',
+ 'name' => 'modperl_clear_stash',
+ 'args' => [
+ {
+ 'type' => 'PerlInterpreter *',
+ 'name' => 'my_perl'
+ },
+ {
+ 'type' => 'apr_pool_t *',
+ 'name' => 'p'
+ },
+ {
+ 'type' => 'const char *',
+ 'name' => 'package'
}
]
},
@@ -3874,6 +3896,28 @@
},
{
'type' => 'apr_size_t *',
+ 'name' => 'len'
+ }
+ ]
+ },
+ {
+ 'return_type' => 'void',
+ 'name' => 'modperl_package2filename',
+ 'args' => [
+ {
+ 'type' => 'apr_pool_t *',
+ 'name' => 'p'
+ },
+ {
+ 'type' => 'const char *',
+ 'name' => 'package'
+ },
+ {
+ 'type' => 'char **',
+ 'name' => 'filename'
+ },
+ {
+ 'type' => 'int *',
'name' => 'len'
}
]

Attachment: signature.asc
Description: OpenPGP digital signature

<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

Recently Viewed:
version-control...    qnx.openqnx.dev...    redhat.rhn.user...    ietf.openpgp/20...    mail.mutt.user/...    web.microformat...    java.sync4j.use...    education.ezpro...    user-groups.blu...    solaris.manager...    org.fitug.debat...    technology.erps...    politics.activi...    linux.redhat.fe...    bug-tracking.ma...    xfce.user/2004-...    hams/2004-11/ms...    kde.users.pim/2...    culture.cooking...    freebsd.devel.x...    gnu.m4.adhoc/20...    ngpt.user/2002-...    apple.fink.deve...   
Home | advertise | OSDir is an inevitable website. super tiny logo

Free Magazines

Cisco News
Receive a free quarterly e-newsletter with exclusive articles on how Cisco IT uses its own products and solutions to enable the business.
subscribe

Systems Management News, the newspaper for IT systems administration and data center managers! Each issue of Systems Management News is chock-full of news and analysis to help you understand what's happening in your field.
subscribe

The Enterprise Newsweekly eWeek is the essential technology information source for builders of e-business.
subscribe

Oracle Magazine Oracle Magazine contains technology strategy articles, sample code, tips, Oracle and partner news, how to articles for developers and DBAs, and more. Oracle (NASDAQ: ORCL) is the world's largest enterprise software company.
subscribe

Total Telecom Total Telecom is "The Economist of the communications industry".
subscribe

Navigation