stas 2003/11/14 22:12:21 Modified: src/docs/1.0/guide porting.pod Changes.pod src/docs/general/perl_reference perl_reference.pod src/docs/general Changes.pod Log: modernize coding techniques sections in porting.pod and perl_reference Submitted by: Brian McCauley <[EMAIL PROTECTED]> Revision Changes Path 1.20 +56 -42 modperl-docs/src/docs/1.0/guide/porting.pod Index: porting.pod =================================================================== RCS file: /home/cvs/modperl-docs/src/docs/1.0/guide/porting.pod,v retrieving revision 1.19 retrieving revision 1.20 diff -u -u -r1.19 -r1.20 --- porting.pod 15 Jul 2003 11:06:02 -0000 1.19 +++ porting.pod 15 Nov 2003 06:12:21 -0000 1.20 @@ -88,7 +88,7 @@ print "Content-type: text/plain\r\n\r\n"; - my $counter = 0; + my $counter = 0; # Explicit initialization technically redundant for (1..5) { increment_counter(); @@ -195,8 +195,8 @@ print "Content-type: text/plain\r\n\r\n"; - my $counter = 0; - + my $counter = 0; # Explicit initialization technically redundant + for (1..5) { increment_counter(); } @@ -228,51 +228,65 @@ It's important to understand that the I<inner subroutine> effect happens only with code that C<Apache::Registry> wraps with a -declaration of the C<handler> subroutine. If you put your code into a -library or module, which the main script require()'s or use()'s, this -effect doesn't occur. - -For example if we move the code from the script into the subroutine -I<run>, place the subroutines into the I<mylib.pl> file, save it in -the same directory as the script itself and require() it, there will -be no problem at all. (Don't forget the C<1;> at the end of the -library or the require() might fail.) - - mylib.pl: - --------- - my $counter; - sub run{ - print "Content-type: text/plain\r\n\r\n"; - $counter = 0; - for (1..5) { - increment_counter(); - } +declaration of the C<handler> subroutine. If you put all your code +into modules, which the main script C<use()>s, this effect doesn't +occur. + +Do not use Perl4-style libraries. Subroutines in such libraries will +only be available to the first script in any given interpreter thread +to C<require()> a library of any given name. This can lead to +confusing sporadic failures. + +The easiest and the fastest way to solve the nested subroutines +problem is to switch every lexically scoped variable foe which you get +the warning for to a package variable. The C<handler> subroutines are +never called re-entrantly and each resides in a package to itself. +Most of the usual disadvantates of package scoped variables are, +therefore, not a concern. Note, however, that whereas explicit +initialization is not always necessary for lexical variables it is +usually necessary for these package variables as they persist in +subsequent executions of the handler and unlike lexical variables, +don't get automatically destroyed at the end of each handler. + + + counter.pl: + ---------- + #!/usr/bin/perl -w + use strict; + + print "Content-type: text/plain\r\n\r\n"; + + # In Perl <5.6 our() did not exist, so: + # use vars qw($counter); + our $counter = 0; # Explicit initialization now necessary + + for (1..5) { + increment_counter(); } + sub increment_counter{ $counter++; print "Counter is equal to $counter !\r\n"; } - 1; - - counter.pl: - ---------- - use strict; - require "./mylib.pl"; - run(); -This solution provides the easiest and the fastest way to solve the -nested subroutines problem, since all you have to do is to move the -code into a separate file, by first wrapping the initial code into -some function that you later will call from the script and keeping the -lexically scoped variables that could cause the problem out of this -function. - -But as a general rule of thumb, unless the script is very short, I -tend to write all the code in external libraries, and to have only a -few lines in the main script. Generally the main script simply calls -the main function of my library. Usually I call it C<init()> or -C<run()>. I don't worry about nested subroutine effects anymore -(unless I create them myself :). +If the variable contains a reference it may hold onto lots of +unecessary memory (or worse) if the reference is left to hang about +until the next call to the same handler. For such variables you +should use C<local> so that the value is removed when the C<handler> +subroutine exits. + + my $query = CGI->new; + +becomes: + + local our $query = CGI->new; + +All this is very interesting but as a general rule of thumb, unless +the script is very short, I tend to write all the code in external +libraries, and to have only a few lines in the main script. Generally +the main script simply calls the main function of my library. Usually +I call it C<init()> or C<run()>. I don't worry about nested +subroutine effects anymore (unless I create them myself :). The section 'L<Remedies for Inner Subroutines|general::perl_reference::perl_reference/Remedies_for_Inner_Subroutines>' discusses 1.37 +5 -0 modperl-docs/src/docs/1.0/guide/Changes.pod Index: Changes.pod =================================================================== RCS file: /home/cvs/modperl-docs/src/docs/1.0/guide/Changes.pod,v retrieving revision 1.36 retrieving revision 1.37 diff -u -u -r1.36 -r1.37 --- Changes.pod 20 Dec 2002 06:13:29 -0000 1.36 +++ Changes.pod 15 Nov 2003 06:12:21 -0000 1.37 @@ -11,6 +11,11 @@ =head1 Ongoing +* porting.pod + + o revamp the "Exposing Apache::Registry secrets" section to use + modern techniques [Brian McCauley <nobull /at/ cpan.org>] + * coding.pod: o clarify the issue with END blocks in packages loaded from the 1.3 +126 -29 modperl-docs/src/docs/general/perl_reference/perl_reference.pod Index: perl_reference.pod =================================================================== RCS file: /home/cvs/modperl-docs/src/docs/general/perl_reference/perl_reference.pod,v retrieving revision 1.2 retrieving revision 1.3 diff -u -u -r1.2 -r1.3 --- perl_reference.pod 9 Feb 2003 02:05:20 -0000 1.2 +++ perl_reference.pod 15 Nov 2003 06:12:21 -0000 1.3 @@ -863,16 +863,17 @@ problem, Perl will always alert you. Given that you have a script that has this problem, what are the ways -to solve it? There are many of them and we will discuss some of them -here. +to solve it? There have been many suggested in the past, and we +discuss some of them here. We will use the following code to show the different solutions. multirun.pl ----------- - #!/usr/bin/perl -w + #!/usr/bin/perl use strict; + use warnings; for (1..3){ print "run: [time $_]\n"; @@ -925,20 +926,27 @@ Counter is equal to 5 ! Counter is equal to 6 ! -Obviously, the C<$counter> variable is not reinitialized on each -execution of run(). It retains its value from the previous execution, -and sub increment_counter() increments that. - -One of the workarounds is to use globally declared variables, with the -C<vars> pragma. +Apparently, the C<$counter> variable is not reinitialized on each +execution of run(), it retains its value from the previous execution, +and increment_counter() increments that. Actually that is not quite +what happens. On each execution of run() a new C<$counter> variable +is initialized to zero but increment_counter() remains bound to the +C<$counter> variable from the first call to run(). + +The simplest of the work-rounds is to use package-scoped variables. +These can be declared using C<our> or, on older versions of Perl, the +C<vars> pragma. Note that whereas using C<my> declaration also +implicitly initializes variables to undefined the C<our> declaration +does not, and so you will probably need to add explicit initialisation +for variables that lacked it. multirun1.pl - ----------- - #!/usr/bin/perl -w + ------------ + #!/usr/bin/perl use strict; - use vars qw($counter); - + use warnings; + for (1..3){ print "run: [time $_]\n"; run(); @@ -946,7 +954,7 @@ sub run { - $counter = 0; + our $counter = 0; increment_counter(); increment_counter(); @@ -977,11 +985,37 @@ problem, since there is no C<my()> (lexically defined) variable used in the nested subroutine. -Another approach is to use fully qualified variables. This is better, -since less memory will be used, but it adds a typing overhead: +In the above example we know C<$counter> is just a simple small +scalar. In the general case variables could reference external +resource handles or large data structures. In that situation the fact +that the variable would not be released immediately when run() +completes could be a problem. To avoid this you can put C<local> in +front of the C<our> declaration of all variables other than simple +scalars. This has the effect of restoring the variable to its +previous value (usually undefined) upon exit from the current scope. +As a side-effect C<local> also initializes the variables to C<undef>. +So, if you recall that thing I said about adding explicit +initialization when you replace C<my> by C<our>, well, you can forget +it again if you replace C<my> with C<local our>. + +Be warned that C<local> will not release circular data structures. If +the original CGI script relied upon process termination to clean up +after it then it will leak memory as a registry script. + +A varient of the package variable approach is not to declare your +variables, but instead to use explicit package qualifiers. This has +the advantage on old versions of Perl that there is no need to load +the C<vars> module, but it adds a significant typing overhead. +Another downside is that you become dependant on the "used only once" +warning to detect typos in variable names. The explicit package name +approach is not really suitable for registry scripts because it +pollutes the C<main::> namespace rather than staying properly within +the namespace that has been allocated. Finally, note that the +overhead of loading the C<vars> module only has to be paid once per +Perl interpreter. multirun2.pl - ----------- + ------------ #!/usr/bin/perl -w use strict; @@ -1019,10 +1053,11 @@ and then submit it for your script to process. multirun3.pl - ----------- - #!/usr/bin/perl -w + ------------ + #!/usr/bin/perl use strict; + use warnings; for (1..3){ print "run: [time $_]\n"; @@ -1056,10 +1091,11 @@ variables in a calling function. multirun4.pl - ----------- - #!/usr/bin/perl -w + ------------ + #!/usr/bin/perl use strict; + use warnings; for (1..3){ print "run: [time $_]\n"; @@ -1092,10 +1128,11 @@ a literal, e.g. I<increment_counter(5)>). multirun5.pl - ----------- - #!/usr/bin/perl -w + ------------ + #!/usr/bin/perl use strict; + use warnings; for (1..3){ print "run: [time $_]\n"; @@ -1120,14 +1157,27 @@ Here is a solution that avoids the problem entirely by splitting the code into two files; the first is really just a wrapper and loader, -the second file contains the heart of the code. +the second file contains the heart of the code. This second file must +go into a directory in your C<@INC>. Some people like to put the +library in the same directory as the script but this assumes that the +current working directory will be equal to the directory where the +script is located and also that C<@INC> will contain C<'.'>, neither +of which are assumptions you should expect to hold in all cases. + +Note that the name chosen for the library must be unique throughout +the entire server and indeed every server on which you many ever +install the script. This solution is probably more trouble than it is +worth - it is only oncluded because it was mentioned in previous +versions of this guide. multirun6.pl - ----------- - #!/usr/bin/perl -w + ------------ + #!/usr/bin/perl use strict; - require 'multirun6-lib.pl' ; + use warnings; + + require 'multirun6-lib.pl'; for (1..3){ print "run: [time $_]\n"; @@ -1138,8 +1188,54 @@ multirun6-lib.pl ---------------- - use strict ; + use strict; + use warnings; + + my $counter; + + sub run { + $counter = 0; + + increment_counter(); + increment_counter(); + } + + sub increment_counter{ + $counter++; + print "Counter is equal to $counter !\n"; + } + + 1 ; + +An alternative verion of the above, that mitigates some of the +disadvantages, is to use a Perl5-style Exporter module rather than a +Perl4-style library. The global uniqueness requirement still applies +to the module name, but at least this is a problem Perl programmers +should already be familiar with when creating modules. + + multirun7.pl + ------------ + #!/usr/bin/perl + use strict; + use warnings; + use My::Multirun7; + + for (1..3){ + print "run: [time $_]\n"; + run(); + } + +Separate file: + + My/Multirun7.pm + --------------- + package My::Multirun7; + use strict; + use warnings; + use base qw( Exporter ); + our @EXPORT = qw( run ); + my $counter; sub run { @@ -1156,7 +1252,8 @@ 1 ; -Now you have at least six workarounds to choose from. +Now you have at least five workarounds to choose from (not counting +numbers 2 and 6). For more information please refer to perlref and perlsub manpages. 1.10 +3 -0 modperl-docs/src/docs/general/Changes.pod Index: Changes.pod =================================================================== RCS file: /home/cvs/modperl-docs/src/docs/general/Changes.pod,v retrieving revision 1.9 retrieving revision 1.10 diff -u -u -r1.9 -r1.10 --- Changes.pod 31 Jul 2002 14:38:31 -0000 1.9 +++ Changes.pod 15 Nov 2003 06:12:21 -0000 1.10 @@ -29,6 +29,9 @@ * perf_reference: + o revamp the "Remedies for Inner Subroutines" section to use modern + techniques [Brian McCauley <nobull /at/ cpan.org>] + o added a section on overriding functions to trace their failure. o inlined Mike Guy's news article about closures
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]