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]