You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by Matt Sergeant <ma...@sergeant.org> on 2000/08/11 13:13:29 UTC

RFC: Apache::Reload

This dates back to discussions nearly a year ago now, from Randal's
initial Stonehenge::Reload, and Doug saying he'd like to see a more
generic Apache::Reload that got rid of the necessity to call reload_me in
your own code... Anyway, I needed this facility too, since I wanted
modules to be able to just "use Apache::Reload" and have them reloaded,
without having to call something myself in some sort of
handler() function, because 90% of my modules don't use a handler() entry
point... So I came up with Apache::Reload based mostly on Apache::StatINC
and partly on Stonehenge::Reload, here's the code, comments welcome:

package Apache::Reload;

use strict;

$Apache::Reload::VERSION = '0.01';

my %Stat; # = ($INC{"Apache/Reload.pm"} => time);
my %INCS;

sub import {
	my $class = shift;
	my ($package,$file) = (caller)[0,1];
	$package =~ s/::/\//g;
	$package .= ".pm";
	
	warn "Apache::Reload: $package loaded me\n";
	
	if (grep /^off$/, @_) {
		delete $INCS{$package};
	}
	else {
		$INCS{$package} = $file;
	}
}

sub handler {
	my $r = shift;
	my $do_undef = ref($r) && 
			(lc($r->dir_config("UndefOnReload") || '') eq
'on');
	
	my $DEBUG = ref($r) && (lc($r->dir_config("ReloadDebug") || '') eq
'on');
	
	while (my($key, $file) = each %INCS) {
		local $^W;
		warn "Apache::Reload: Checking mtime of $key\n" if $DEBUG;
		
		my $mtime = (stat $file)[9];
		warn("Apache::Reload: Can't locate $file\n"),next 
				unless defined $mtime and $mtime;
		
		unless (defined $Stat{$file}) {
			$Stat{$file} = $^T;
		}
		
		if ($mtime > $Stat{$file}) {
			if ($do_undef and $key =~ /\.pm$/) {
				require Apache::Symbol;
				my $class =
Apache::Symbol::file2class($key);

$class->Apache::Symbol::undef_functions(undef, 1);
			}
			delete $INC{$key};
			require $key;
			warn("Apache::Reload: process $$ reloading
$key\n")
					if $DEBUG;
		}
		$Stat{$file} = $mtime;
	}
	
	return 1;
}

1;
__END__

=head1 NAME

Apache::Reload - Reload this module on each request (if modified)

=head1 SYNOPSIS

In httpd.conf:

  PerlInitHandler Apache::StatINC

Then your module:

  package My::Apache::Module;

  use Apache::Reload;
  
  sub handler { ... }
  
  1;

=head1 DESCRIPTION

This module is an adaptation of Randall Schwartz's Stonehenge::Reload
module that attempts to be a little more intuitive and makes the usage
easier. Like Apache::StatINC it must be installed as an Init Handler,
but unlike StatINC it must also be used by the module you want reloading.

If you want to temporarily turn off reloading of a module (which is 
slightly problematic since it won't happen until the next hit on the
same server because of the way this thing works) you can use the 'off'
option:

  use Apache::Reload 'off';

Obviously you wouldn't do that generally, but it can be useful if you 
intend to make large changes to a particular module.

=head1 AUTHOR

Matt Sergeant, matt@sergeant.org

=head1 SEE ALSO

Apache::StatINC, Stonehenge::Reload

=cut


-- 
<Matt/>

Fastnet Software Ltd. High Performance Web Specialists
Providing mod_perl, XML, Sybase and Oracle solutions
Email for training and consultancy availability.
http://sergeant.org | AxKit: http://axkit.org


Re: RFC: Apache::Reload

Posted by Ken Williams <ke...@forum.swarthmore.edu>.
matt@sergeant.org (Matt Sergeant) wrote:
>On Fri, 11 Aug 2000, Matt Sergeant wrote:
>> This dates back to discussions nearly a year ago now, from Randal's
>> initial Stonehenge::Reload, and Doug saying he'd like to see a more
>> generic Apache::Reload that got rid of the necessity to call reload_me in
>> your own code... Anyway, I needed this facility too, since I wanted
>> modules to be able to just "use Apache::Reload" and have them reloaded,
>> without having to call something myself in some sort of
>> handler() function, because 90% of my modules don't use a handler() entry
>> point... So I came up with Apache::Reload based mostly on Apache::StatINC
>> and partly on Stonehenge::Reload, here's the code, comments welcome:
>
>I'm going to take a lack of negative replies as positive... I'll upload to
>CPAN later.

Hi Matt,

Is it possible that this functionality should be integrated into
Apache::StatInc?  Perhaps with a couple of new directives to control
behavior?

That would be preferable to starting a new module, I think.




Re: RFC: Apache::Reload

Posted by Matt Sergeant <ma...@sergeant.org>.
On Fri, 11 Aug 2000, Matt Sergeant wrote:

> This dates back to discussions nearly a year ago now, from Randal's
> initial Stonehenge::Reload, and Doug saying he'd like to see a more
> generic Apache::Reload that got rid of the necessity to call reload_me in
> your own code... Anyway, I needed this facility too, since I wanted
> modules to be able to just "use Apache::Reload" and have them reloaded,
> without having to call something myself in some sort of
> handler() function, because 90% of my modules don't use a handler() entry
> point... So I came up with Apache::Reload based mostly on Apache::StatINC
> and partly on Stonehenge::Reload, here's the code, comments welcome:

I'm going to take a lack of negative replies as positive... I'll upload to
CPAN later.

-- 
<Matt/>

Fastnet Software Ltd. High Performance Web Specialists
Providing mod_perl, XML, Sybase and Oracle solutions
Email for training and consultancy availability.
http://sergeant.org | AxKit: http://axkit.org


Re: getting rid of nested sub lexical problem

Posted by ch...@sportsrocket.com.
Converting  "sub foo" into "local *foo = sub" won't work when the function
is called before is declared, because the local only works for the rest
of the block, not the entire block. i.e. you can't do

  foo();
  local *foo = sub {
     ...
  };

Plus, I think you usually need the trailing semicolon anyway.

--Chris

On Wed, 16 Aug 2000, Dan Campbell wrote:

> chris@sportsrocket.com wrote:
> > 
> > Due to forgetfulness I was recently bitten by the infamous "my() Scoped
> > Variable in Nested Subroutines" problem using Apache::Registry, and it got
> > me thinking about whether it is fixable.
> > 
> > >From the diagnostics:
> >   This problem can usually be solved by making the inner subroutine
> >   anonymous, using the sub {} syntax.  When inner anonymous subs that
> >   reference variables in outer subroutines are called or referenced, they
> >   are automatically rebound to the current values of such variables.
> > 
> > I think it should be possible for Registry to pre-process the source code
> > to turn all top-level named subroutines into sub refs. For example,
> > convert subroutines of the form
> > 
> >   sub foo { <CODE> }
> > 
> > into
> > 
> >   sub foo { &{ sub { <CODE> } } }
> [snip]
> 
> I think it would be easier to convert
> 
>    sub foo
> 
> into
> 
>    local *foo = sub
> 
> 
> That way, you don't have to parse for the matching right brace.
> Something like:
> 
>    s/sub\s+(\w+)/local *\1 = sub/msg; # untested - just a guess
> 
> might do the trick.
> 
> --
> Dan Campbell
> 


Re: getting rid of nested sub lexical problem

Posted by Dan Campbell <mo...@danofsteel.com>.
chris@sportsrocket.com wrote:
> 
> Due to forgetfulness I was recently bitten by the infamous "my() Scoped
> Variable in Nested Subroutines" problem using Apache::Registry, and it got
> me thinking about whether it is fixable.
> 
> >From the diagnostics:
>   This problem can usually be solved by making the inner subroutine
>   anonymous, using the sub {} syntax.  When inner anonymous subs that
>   reference variables in outer subroutines are called or referenced, they
>   are automatically rebound to the current values of such variables.
> 
> I think it should be possible for Registry to pre-process the source code
> to turn all top-level named subroutines into sub refs. For example,
> convert subroutines of the form
> 
>   sub foo { <CODE> }
> 
> into
> 
>   sub foo { &{ sub { <CODE> } } }
[snip]

I think it would be easier to convert

   sub foo

into

   local *foo = sub


That way, you don't have to parse for the matching right brace.
Something like:

   s/sub\s+(\w+)/local *\1 = sub/msg; # untested - just a guess

might do the trick.

--
Dan Campbell

Re: getting rid of nested sub lexical problem

Posted by "Randal L. Schwartz" <me...@stonehenge.com>.
>>>>> "Douglas" == Douglas Wilson <do...@racesearch.com> writes:

Douglas> That gives me a 'Variable "$counter" may be unavailable at ./tst2 line 17.'
Douglas> warning. And its sort of obfuscated (to me, anyway). I'd probably do it this
Douglas> way:

Douglas> #!/usr/local/bin/perl -w

Douglas> use strict;

Douglas> #!/usr/bin/perl -w
Douglas> use strict;

Douglas> for (1..3){
Douglas>     print "run: [time $_]\n";
Douglas>     run();
Douglas> }

Douglas> {
Douglas>  my $counter;

Douglas>  sub run {
Douglas>     $counter = 0;

Douglas>     print $counter,"\n";

Douglas>     increment_counter();
Douglas>     increment_counter();

Douglas>  }
Douglas>  sub increment_counter {
Douglas>        $counter++;
Douglas>        print "Counter is equal to $counter !\n";
Douglas>  }
Douglas> }

But *that* won't work inside the big subroutine created by
Apache::Registry (which is NOT the same as mod_perl, even though too
many people think so :), and that was the original point.  I'm
not sure the new code fixes it.  I think the problem is intractable,
or equivalent to the Travelling Salesman and the Farmer's Daughter
problem or something like that. :)

-- 
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<me...@stonehenge.com> <URL:http://www.stonehenge.com/merlyn/>
Perl/Unix/security consulting, Technical writing, Comedy, etc. etc.
See PerlTraining.Stonehenge.com for onsite and open-enrollment Perl training!

RE: getting rid of nested sub lexical problem

Posted by ch...@sportsrocket.com.
I would agree, except that rearranging subroutines and variable
declarations in some automated way seems to be a more difficult (and
potentially buggier) task.

--Chris

On Wed, 16 Aug 2000, Douglas Wilson wrote:
> 
> That gives me a 'Variable "$counter" may be unavailable at ./tst2 line 17.'
> warning. And its sort of obfuscated (to me, anyway). I'd probably do it this
> way:
> 
> #!/usr/local/bin/perl -w
> 
> use strict;
> 
> #!/usr/bin/perl -w
> use strict;
> 
> for (1..3){
>     print "run: [time $_]\n";
>     run();
> }
> 
> {
>  my $counter;
> 
>  sub run {
>     $counter = 0;
> 
>     print $counter,"\n";
> 
>     increment_counter();
>     increment_counter();
> 
>  }
>  sub increment_counter {
>        $counter++;
>        print "Counter is equal to $counter !\n";
>  }
> }
> 


RE: getting rid of nested sub lexical problem

Posted by Douglas Wilson <do...@racesearch.com>.

> -----Original Message-----
> From: chris@sportsrocket.com [mailto:chris@sportsrocket.com]
> Sent: Wednesday, August 16, 2000 2:17 AM
> To: modperl@apache.org
> Subject: getting rid of nested sub lexical problem
>
> Below is a processed version of the increment_counter example from the
> guide that works as expected.
>
> --Chris
>
>   #!/usr/bin/perl -w
>   use strict;
>
>   for (1..3){
>       print "run: [time $_]\n";
>       run();
>   }
>
>   sub run {
>
>       my $counter = 0;
>
>       increment_counter();
>       increment_counter();
>
>       sub increment_counter {&{sub{
>           $counter++;
>           print "Counter is equal to $counter !\n";
>       }}}
>
>   } # end of sub run
>
>

That gives me a 'Variable "$counter" may be unavailable at ./tst2 line 17.'
warning. And its sort of obfuscated (to me, anyway). I'd probably do it this
way:

#!/usr/local/bin/perl -w

use strict;

#!/usr/bin/perl -w
use strict;

for (1..3){
    print "run: [time $_]\n";
    run();
}

{
 my $counter;

 sub run {
    $counter = 0;

    print $counter,"\n";

    increment_counter();
    increment_counter();

 }
 sub increment_counter {
       $counter++;
       print "Counter is equal to $counter !\n";
 }
}


Re: getting rid of nested sub lexical problem

Posted by Chris Nokleberg <cj...@rock.pickem.com>.
On Thu, 21 Dec 2000, Doug MacEachern wrote:

> On Thu, 19 Oct 2000, Chris Nokleberg wrote:
> 
> > Following up on my post on this subject a couple of months ago, here is a
> > proof-of-concept drop-in replacement for Apache::Registry that eliminates
> > the "my() Scoped Variable in Nested Subroutine" problem.
> 
> nice hack!
>  
> > It requires PERL5OPT = "-d" and PERL5DB = "sub DB::DB {}" environment
> > variables set when starting the mod_perl-enabled httpd. This enables the
> > %DB::sub hash table that holds subroutine start and end line info. I
> > presume that this has some negative (marginal?) impact on performance. If
> > someone knows of a better way to reliably figure out where a subroutine
> > starts and ends, please let me know.
> 
> there is quite a bit of overhead when -d is enabled.  have a look at
> Apache::DB.xs, there's in init_debugger() function todo what -d does.  if
> another method was added to turn it off, after the registry script was
> compiled, the overhead would be reduced a great deal.

oooh, cool. I've added this to Apache::DB.xs:

int
stop_debugger()

    CODE:
    if (PL_perldb) {
        PL_perldb = 0;
        RETVAL = TRUE;
    }
    else
        RETVAL = FALSE;

    OUTPUT:
    RETVAL


It appears that setting PL_perldb to zero is all that's required to turn
off the debugger! I thought I might have to fiddle with the symbol tables
but this seems to work.

I've attached the latest version of Apache::NiceRegistry (suggestions
welcome) below. Not sure this is the right thing to do , but I call
Apache::DB::init_debugger directly because I need the return value (don't
want to stop_debugger if it was turned on outside of this handler), and I
don't want the warning that Apache::DB->init produces.

Also, why does Apache::DB unset $Apache::Registry::MarkLine?

Thanks,
Chris

--------------------------------------

package Apache::NiceRegistry;

use base Apache::RegistryNG;
use strict;
use Apache::DB ();

$Apache::Registry::MarkLine = 1;

my %skip_subs = map { $_ => 1 } qw( handler BEGIN END CHECK INIT );

sub compile {
    my ($pr, $eval) = @_;
    $eval ||= $pr->{'sub'};

    my $init_db = Apache::DB::init_debugger();
    $pr->SUPER::compile($eval);
    Apache::DB::stop_debugger() if $init_db;

    my $package = $pr->namespace;
    my @lines = split /\n/, $$eval;

    foreach my $sub (grep /^$\Qpackage\E/, keys %DB::sub) {
        my ($name) = $sub =~ /:([^:]+)$/;
        next if $skip_subs{$name};

        my ($start, $end) = $DB::sub{$sub} =~ /:(\d+)-(\d+)$/;
        $lines[$start + 1] =~ s:(sub[^\{]+\{):$1&\{sub\{:;
        $lines[$end + 1]   =~ s:\}(?!.*\})$:\}\}\}:;
    }

    $$eval = join "\n", @lines;
    $pr->flush_namespace($package);
    $pr->SUPER::compile($eval);
}

1;



Re: getting rid of nested sub lexical problem

Posted by Doug MacEachern <do...@covalent.net>.
On Thu, 19 Oct 2000, Chris Nokleberg wrote:

> Following up on my post on this subject a couple of months ago, here is a
> proof-of-concept drop-in replacement for Apache::Registry that eliminates
> the "my() Scoped Variable in Nested Subroutine" problem.

nice hack!
 
> It requires PERL5OPT = "-d" and PERL5DB = "sub DB::DB {}" environment
> variables set when starting the mod_perl-enabled httpd. This enables the
> %DB::sub hash table that holds subroutine start and end line info. I
> presume that this has some negative (marginal?) impact on performance. If
> someone knows of a better way to reliably figure out where a subroutine
> starts and ends, please let me know.

there is quite a bit of overhead when -d is enabled.  have a look at
Apache::DB.xs, there's in init_debugger() function todo what -d does.  if
another method was added to turn it off, after the registry script was
compiled, the overhead would be reduced a great deal.


Re: getting rid of nested sub lexical problem

Posted by Chris Nokleberg <ch...@sportsrocket.com>.
Following up on my post on this subject a couple of months ago, here is a
proof-of-concept drop-in replacement for Apache::Registry that eliminates
the "my() Scoped Variable in Nested Subroutine" problem.

It requires PERL5OPT = "-d" and PERL5DB = "sub DB::DB {}" environment
variables set when starting the mod_perl-enabled httpd. This enables the
%DB::sub hash table that holds subroutine start and end line info. I
presume that this has some negative (marginal?) impact on performance. If
someone knows of a better way to reliably figure out where a subroutine
starts and ends, please let me know.

The processed code will probably generate harmless warnings if you have
them turned on.

I'm interested if there is support for turning this into a
production-quality module. Better yet, perhaps the existence of such an
ridiculous hack could spur the perl maintainers to provide a cleaner
solution.

--Chris

package My::CleanerRegistry;
use base Apache::RegistryNG;
use strict;

my %skip_subs = map { $_ => 1 } qw( handler BEGIN END );

sub compile {
    my ($pr, $eval) = @_;
    $eval ||= $pr->{'sub'};
    $pr->SUPER::compile($eval);
    my $package = quotemeta($pr->namespace);
    my @lines = split /\n/, $$eval;
    foreach my $sub (grep /^$package/, keys %DB::sub) {
        my ($name) = $sub =~ /:([^:]+)$/;
        next if $skip_subs{$name};
        my ($start, $end) = $DB::sub{$sub} =~ /:(\d+)-(\d+)$/;
        $start++, $end++;
        $lines[$start] =~ s:(sub[^\{]+\{):$1&\{sub\{:;
        $lines[$end]   =~ s:\}(?!.*\})$:\}\}\}:;
    }
    $$eval = join "\n", @lines;
    $pr->flush_namespace($package);
    my $rv = $pr->SUPER::compile($eval);
}

1;



On Wed, 16 Aug 2000 chris@sportsrocket.com wrote:

> Due to forgetfulness I was recently bitten by the infamous "my() Scoped
> Variable in Nested Subroutines" problem using Apache::Registry, and it got
> me thinking about whether it is fixable.
> 
> From the diagnostics:
>   This problem can usually be solved by making the inner subroutine
>   anonymous, using the sub {} syntax.  When inner anonymous subs that
>   reference variables in outer subroutines are called or referenced, they
>   are automatically rebound to the current values of such variables.
> 
> I think it should be possible for Registry to pre-process the source code
> to turn all top-level named subroutines into sub refs. For example,
> convert subroutines of the form
> 
>   sub foo { <CODE> }
> 
> into
> 
>   sub foo { &{ sub { <CODE> } } }
> 
> Are there cases for which this would not work? I have a sneaking suspicion
> that I am missing something important. If it is a reasonable solution, I
> imagine there are better ways to do this transformation than fancy
> regexps? Parse/modify/deparse?
> 
> Below is a processed version of the increment_counter example from the
> guide that works as expected.
> 
> --Chris
> 
>   #!/usr/bin/perl -w
>   use strict;
> 
>   for (1..3){
>       print "run: [time $_]\n";
>       run();
>   }
> 
>   sub run {
> 
>       my $counter = 0;
> 
>       increment_counter();
>       increment_counter();
> 
>       sub increment_counter {&{sub{
>           $counter++;
>           print "Counter is equal to $counter !\n";
>       }}}
> 
>   } # end of sub run
> 
> 


getting rid of nested sub lexical problem

Posted by ch...@sportsrocket.com.
Due to forgetfulness I was recently bitten by the infamous "my() Scoped
Variable in Nested Subroutines" problem using Apache::Registry, and it got
me thinking about whether it is fixable.

Re: RFC: Apache::Reload

Posted by Matt Sergeant <ma...@sergeant.org>.
On Tue, 15 Aug 2000, Ask Bjoern Hansen wrote:

> On Fri, 11 Aug 2000, Matt Sergeant wrote:
> 
> > sub handler {
> > 	my $r = shift;
> > 	my $do_undef = ref($r) && 
> > 			(lc($r->dir_config("UndefOnReload") || '') eq
> > 'on');
> 
> Would be neat to have this as an import parameter.

I've removed it from 0.02, apparently its unnecessary.

> I don't know why I never got it into StatINC, but I've changed that
> to have a parameter for what files to reload and other things like
> that. It would make a lot of sense to merge your reload and StatINC
> I think, with an on/off switch for "old StatINC behavior", possibly
> invoked by loading Apache::StatINC (which would load
> Apache::Reload).

See 0.02, its now a fully fledged StatINC replacement along with the new
functionality.

-- 
<Matt/>

Fastnet Software Ltd. High Performance Web Specialists
Providing mod_perl, XML, Sybase and Oracle solutions
Email for training and consultancy availability.
http://sergeant.org | AxKit: http://axkit.org


Re: RFC: Apache::Reload

Posted by Ask Bjoern Hansen <as...@valueclick.com>.
On Fri, 11 Aug 2000, Matt Sergeant wrote:

> sub handler {
> 	my $r = shift;
> 	my $do_undef = ref($r) && 
> 			(lc($r->dir_config("UndefOnReload") || '') eq
> 'on');

Would be neat to have this as an import parameter.

I don't know why I never got it into StatINC, but I've changed that
to have a parameter for what files to reload and other things like
that. It would make a lot of sense to merge your reload and StatINC
I think, with an on/off switch for "old StatINC behavior", possibly
invoked by loading Apache::StatINC (which would load
Apache::Reload).

It should be called Apache::Reload and be distributed in the
mod_perl package, IMO.


 - ask

-- 
ask bjoern hansen - <http://www.netcetera.dk/~ask/>
more than 70M impressions per day, <http://valueclick.com>