http://www.perlmonks.org?node_id=562756


in reply to mod_perl / Apache::Registry accidental closures

Good explanation. I'd suggest adding something that explicitly explains how to avoid this problem.

The basic solution is to wrap all top-level code that isn't inside a subroutine in a new sub, and then fixing all the problems with global variables.

In this case, something like:

use strict; use warnings; main(); # <----- change + here sub main { # <----- change + here my $foo = 5; print "Content-type: text/plain\n"; print "Content-disposition: inline; filename=foo.txt\n\n"; printf "Package: %s\n", __PACKAGE__; printf "[%s] Before: %s\n", $$, $foo; badness(5); printf "[%s] After: %s\n", $$, $foo; } # <----- change + here sub badness { my $val = shift; printf "[%s] badness: %s\n", $$, $foo; $foo += $val; }

That will not run, so we need to fix the reference to $foo in badness:

use strict; use warnings; main(); main(); main(); sub main { my $foo = 5; print "Content-type: text/plain\n"; print "Content-disposition: inline; filename=foo.txt\n\n"; printf "Package: %s\n", __PACKAGE__; printf "[%s] Before: %s\n", $$, $foo; badness(5, $foo); # <----- change + here printf "[%s] After: %s\n", $$, $foo; } sub badness { my $val = shift; our $foo; # <----- clever + trick here(*) local *foo = \shift; # <- printf "[%s] badness: %s\n", $$, $foo; $foo += $val; }

Real-life scripts will probably be harder, so make sure to run perl -c your_script.pl early and often :^)

Update: As ikegami pointed out, using my $foo = shift in badness runs, but it doesn't act the same. The (*)clever trick suggested by ikegami is a good way to circumvent that. Later iterations should probably move to a more functional approach, by making the subroutines free of side effects.

Replies are listed 'Best First'.
Re^2: mod_perl / Apache::Registry accidental closures
by perrin (Chancellor) on Jul 21, 2006 at 17:52 UTC
    The easiest solution by far is to just pass all the required variables to your subs. This is a good programming practice anyway.
      I agree - that's the only change I made to the alternative that I recommended at the end of the post.

      In that case I passed the values by reference, to preserve the behaviour of the original function (which modified the passed variable.)

        Yes, I agree with you both (of course).

        The tricky bit about just passing in the variables is that you'd need to redefine the interface of the sub (that is, return the modified values instead of editing in place). Which - for the people who stand to benefit the most from this Tutorial - might be a lot of work. If your script began with global variables in the first place, there's a good chance the rest of the program is procedural in nature.

        imp, Your solution with passing by reference is even better than ikegami's, in that I expect it to be easier to understand.