gozer 2004/09/09 15:16:38
Modified: . Changes ModPerl-Registry/lib/ModPerl RegistryCooker.pm lib/Apache Reload.pm src/modules/perl modperl_cmd.c modperl_util.c modperl_util.h t/response/TestModules reload.pm todo features_optimization release xs/ModPerl/Util ModPerl__Util.h xs/maps modperl_functions.map xs/tables/current/ModPerl FunctionTable.pm Log: Added ModPerl::Util::unload_package() to remove a loaded package as thoroughly as possible by clearing it's stash. Adjusted <Perl> sections, Apache::Reload and ModPerl::Registry to use the new function. Revision Changes Path 1.480 +3 -0 modperl-2.0/Changes Index: Changes =================================================================== RCS file: /home/cvs/modperl-2.0/Changes,v retrieving revision 1.479 retrieving revision 1.480 diff -u -r1.479 -r1.480 --- Changes 9 Sep 2004 18:48:03 -0000 1.479 +++ Changes 9 Sep 2004 22:16:37 -0000 1.480 @@ -12,6 +12,9 @@ =item 1.99_17-dev +Added ModPerl::Util::unload_package() to remove a loaded package +as thoroughly as possible by clearing it's stash. [Gozer] + fix Apache->request($r) to be set-able even w/: PerlOptions -GlobalRequest [Stas] 1.51 +1 -42 modperl-2.0/ModPerl-Registry/lib/ModPerl/RegistryCooker.pm Index: RegistryCooker.pm =================================================================== RCS file: /home/cvs/modperl-2.0/ModPerl-Registry/lib/ModPerl/RegistryCooker.pm,v retrieving revision 1.50 retrieving revision 1.51 diff -u -r1.50 -r1.51 --- RegistryCooker.pm 27 Jun 2004 21:26:45 -0000 1.50 +++ RegistryCooker.pm 9 Sep 2004 22:16:37 -0000 1.51 @@ -525,48 +525,7 @@ my $self = shift; $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::unload_package($self->{PACKAGE}); } 1.16 +13 -31 modperl-2.0/lib/Apache/Reload.pm Index: Reload.pm =================================================================== RCS file: /home/cvs/modperl-2.0/lib/Apache/Reload.pm,v retrieving revision 1.15 retrieving revision 1.16 diff -u -r1.15 -r1.16 --- Reload.pm 9 Sep 2004 18:29:09 -0000 1.15 +++ Reload.pm 9 Sep 2004 22:16:37 -0000 1.16 @@ -27,7 +27,9 @@ use Apache::ServerUtil; use Apache::RequestUtil; -use vars qw(%INCS %Stat $TouchTime %UndefFields); +use ModPerl::Util (); + +use vars qw(%INCS %Stat $TouchTime); %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"; - } } sub unregister_module { @@ -116,15 +120,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"; - } } } } @@ -158,29 +153,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::unload_package($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; 1.65 +1 -4 modperl-2.0/src/modules/perl/modperl_cmd.c Index: modperl_cmd.c =================================================================== RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_cmd.c,v retrieving revision 1.64 retrieving revision 1.65 diff -u -r1.64 -r1.65 --- modperl_cmd.c 23 Aug 2004 21:16:27 -0000 1.64 +++ modperl_cmd.c 9 Sep 2004 22:16:37 -0000 1.65 @@ -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_package_unload(aTHX_ pkg_name); } if (status != OK) { 1.80 +95 -54 modperl-2.0/src/modules/perl/modperl_util.c Index: modperl_util.c =================================================================== RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_util.c,v retrieving revision 1.79 retrieving revision 1.80 diff -u -r1.79 -r1.80 --- modperl_util.c 9 Sep 2004 15:08:38 -0000 1.79 +++ modperl_util.c 9 Sep 2004 22:16:37 -0000 1.80 @@ -303,6 +303,59 @@ free(handles); } +/* XXX: There is no XS accessible splice() */ +static void modperl_av_remove_entry(pTHX_ AV *av, I32 index) +{ + I32 i; + AV *tmpav = newAV(); + + /* stash the entries _before_ the item to delete */ + for (i=0; i<=index; i++) { + av_store(tmpav, i, SvREFCNT_inc(av_shift(av))); + } + + /* make size at the beginning of the array */ + av_unshift(av, index-1); + + /* add stashed entries back */ + for (i=0; i<index; i++) { + av_store(av, i, *av_fetch(tmpav, i, 0)); + } + + SvREFCNT_dec(tmpav); +} + +static void modperl_package_unload_dynamic(pTHX_ const char *package, + I32 dl_index) +{ + AV *librefs = get_av(dl_librefs, 0); + SV *libref = *av_fetch(librefs, dl_index, 0); + + modperl_sys_dlclose((void *)SvIV(libref)); + + /* remove package from @dl_librefs and @dl_modules */ + modperl_av_remove_entry(aTHX_ get_av(dl_librefs, 0), dl_index); + modperl_av_remove_entry(aTHX_ get_av(dl_modules, 0), dl_index); + + return; +} + +static int modperl_package_is_dynamic(pTHX_ const char *package, + I32 *dl_index) +{ + I32 i; + AV *modules = get_av(dl_modules, FALSE); + + for (i=0; i<av_len(modules); i++) { + SV *module = *av_fetch(modules, i, 0); + if (strEQ(package, SvPVX(module))) { + *dl_index = i; + return TRUE; + } + } + return FALSE; +} + modperl_cleanup_data_t *modperl_cleanup_data_new(apr_pool_t *p, void *data) { modperl_cleanup_data_t *cdata = @@ -526,60 +579,6 @@ return (svp && *svp != &PL_sv_undef) ? 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); \ @@ -784,4 +783,46 @@ } return array; +} + +/* Remove a package from %INC */ +static void modperl_package_delete_from_inc(pTHX_ const char *package) +{ + int len; + char *filename = package2filename(package, &len); + hv_delete(GvHVn(PL_incgv), filename, len, G_DISCARD); + free(filename); +} + +/* Destroy a package's stash */ +static void modperl_package_clear_stash(pTHX_ const char *package) +{ + HV *stash; + if ((stash = gv_stashpv(package, FALSE))) { + HE *he; + I32 len; + char *key; + hv_iterinit(stash); + while ((he = hv_iternext(stash))) { + key = hv_iterkey(he, &len); + /* We skip entries ending with ::, they are sub-stashes */ + if (len > 2 && key[len] != ':' && key[len-1] != ':') { + hv_delete(stash, key, len, G_DISCARD); + } + } + } +} + +/* Unload a module as completely and cleanly as possible */ +void modperl_package_unload(pTHX_ const char *package) +{ + I32 dl_index; + + modperl_package_clear_stash(aTHX_ package); + modperl_package_delete_from_inc(aTHX_ package); + + if (modperl_package_is_dynamic(aTHX_ package, &dl_index)) { + modperl_package_unload_dynamic(aTHX_ package, dl_index); + } + } 1.67 +1 -0 modperl-2.0/src/modules/perl/modperl_util.h Index: modperl_util.h =================================================================== RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_util.h,v retrieving revision 1.66 retrieving revision 1.67 diff -u -r1.66 -r1.67 --- modperl_util.h 22 Aug 2004 20:47:37 -0000 1.66 +++ modperl_util.h 9 Sep 2004 22:16:37 -0000 1.67 @@ -109,6 +109,7 @@ SV *modperl_apr_array_header2avrv(pTHX_ apr_array_header_t *array); apr_array_header_t *modperl_avrv2apr_array_header(pTHX_ apr_pool_t *p, SV *avrv); +void modperl_package_unload(pTHX_ const char *package); #if defined(MP_TRACE) && defined(APR_HAS_THREADS) #define MP_TRACEf_TID "/tid 0x%lx" #define MP_TRACEv_TID (unsigned long)apr_os_thread_current() 1.4 +0 -1 modperl-2.0/t/response/TestModules/reload.pm Index: reload.pm =================================================================== RCS file: /home/cvs/modperl-2.0/t/response/TestModules/reload.pm,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- reload.pm 9 Sep 2004 18:29:09 -0000 1.3 +++ reload.pm 9 Sep 2004 22:16:38 -0000 1.4 @@ -29,5 +29,4 @@ PerlModule Apache::Reload PerlInitHandler Apache::TestHandler::same_interp_fixup Apache::Reload PerlSetVar ReloadDebug Off -PerlSetVar ReloadConstantRedefineWarnings Off PerlSetVar ReloadAll Off 1.4 +3 -0 modperl-2.0/todo/features_optimization Index: features_optimization =================================================================== RCS file: /home/cvs/modperl-2.0/todo/features_optimization,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- features_optimization 4 Mar 2004 01:04:28 -0000 1.3 +++ features_optimization 9 Sep 2004 22:16:38 -0000 1.4 @@ -15,3 +15,6 @@ * currently when ithreads-enabled perl is used anon-sub handlers are always deparsed and non-cached. there are several cases when this can be optimized. See modperl_handler_new_anon in modperl_handler.c + +* modperl_package_unload() and modperl_xs_dl_*() share some duplicated + logic. The managment of DynaLoaded modules could be somewhat cleaner. 1.55 +0 -4 modperl-2.0/todo/release Index: release =================================================================== RCS file: /home/cvs/modperl-2.0/todo/release,v retrieving revision 1.54 retrieving revision 1.55 diff -u -r1.54 -r1.55 --- release 25 Aug 2004 23:47:33 -0000 1.54 +++ release 9 Sep 2004 22:16:38 -0000 1.55 @@ -100,10 +100,6 @@ not sure when. we need to ping him every so often. but it'll probably won't happen by the time we release 2.0. -* Apache::Reload - - needs to handle properly redefined subs warnings - owner: gozer - * Apache->unescape_url{_info}: not yet implemented. should be moved to Apache::Util (or may be APR::URI?) 1.7 +2 -0 modperl-2.0/xs/ModPerl/Util/ModPerl__Util.h Index: ModPerl__Util.h =================================================================== RCS file: /home/cvs/modperl-2.0/xs/ModPerl/Util/ModPerl__Util.h,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- ModPerl__Util.h 25 Aug 2004 21:51:21 -0000 1.6 +++ ModPerl__Util.h 9 Sep 2004 22:16:38 -0000 1.7 @@ -29,5 +29,7 @@ #define mpxs_ModPerl__Util_current_callback \ modperl_callback_current_callback_get +#define mpxs_ModPerl__Util_unload_package(pkg) \ + modperl_package_unload(aTHX_ pkg) 1.86 +1 -0 modperl-2.0/xs/maps/modperl_functions.map Index: modperl_functions.map =================================================================== RCS file: /home/cvs/modperl-2.0/xs/maps/modperl_functions.map,v retrieving revision 1.85 retrieving revision 1.86 diff -u -r1.85 -r1.86 --- modperl_functions.map 25 Aug 2004 21:51:21 -0000 1.85 +++ modperl_functions.map 9 Sep 2004 22:16:38 -0000 1.86 @@ -7,6 +7,7 @@ mpxs_ModPerl__Util_untaint | | ... DEFINE_exit | | int:status=0 char *:DEFINE_current_callback + DEFINE_unload_package | | const char *:package MODULE=ModPerl::Global mpxs_ModPerl__Global_special_list_call 1.178 +15 -1 modperl-2.0/xs/tables/current/ModPerl/FunctionTable.pm Index: FunctionTable.pm =================================================================== RCS file: /home/cvs/modperl-2.0/xs/tables/current/ModPerl/FunctionTable.pm,v retrieving revision 1.177 retrieving revision 1.178 diff -u -r1.177 -r1.178 --- FunctionTable.pm 8 Sep 2004 00:42:02 -0000 1.177 +++ FunctionTable.pm 9 Sep 2004 22:16:38 -0000 1.178 @@ -2,7 +2,7 @@ # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # ! WARNING: generated by ModPerl::ParseSource/0.01 -# ! Mon Aug 30 22:40:23 2004 +# ! Wed Sep 8 15:12:22 2004 # ! do NOT edit, any changes will be lost ! # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -3875,6 +3875,20 @@ { 'type' => 'apr_size_t *', 'name' => 'len' + } + ] + }, + { + 'return_type' => 'void', + 'name' => 'modperl_package_unload', + 'args' => [ + { + 'type' => 'PerlInterpreter *', + 'name' => 'my_perl' + }, + { + 'type' => 'const char *', + 'name' => 'package' } ] },