public
Created

perl: DESTROY called in unexpected sequence

  • Download Gist
unexpectedDestroySequence.pl
Perl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
#!/usr/bin/perl
use strict;
use warnings;
 
=head1 DESTROY called in unexpected sequence
 
See http://stackoverflow.com/questions/11427281/destroy-called-in-unexpected-sequence
for discussion.
 
I started out noticing something odd about "Scope::Guard". If I undef
a "$guard" variable as the very last statement in a sub, the guard's sub gets
called later than I expect. If I don't undef it, or if I do something
(anything) after "undef $guard", it gets called when the reference goes out of
scope as documented. I wonder why.
 
In the following I've made my own poor-man's "Scope::Guard" ("SGuard" below)
just to make the example as simple as possible. You can also use "Scope::Guard"
and get the exact same results that are unexpected at least to me.
 
I'm expecting that the "$mySubGuard" inside "mySub()" should be destroyed first
and the "$scopeGuard" in the scope that calls "mySub()" should be destroyed
last. And so get output like:
 
shouldDestroyFirst
mySub returned a undefined value
shouldDestroyLast
done
 
Which I do get if I uncomment the "undef $mySubGuard" line. But instead, using
the snippet below, I get this output:
 
mySub returned a undefined value
shouldDestroyLast
shouldDestroyFirst
done
 
So from the output, it looks like the "$mySubGuard" from "mySub()" is destoyed
after variables local to the outer scope are destroyed.
 
Why does behavior differ just because I undef a variable that is about to go
out of scope anyway? And why does it matter whether something is done
afterwards?
 
=cut
 
my $sClass = 'SGuard';
# Uncomment to use Scope::Guard instead:
# use Scope::Guard; $sClass = 'Scope::Guard';
 
package SGuard;
 
sub new {
my ($class, $sub) = @_;
return bless { sub => $sub }, $class;
}
 
sub DESTROY {
my ($self) = @_;
$self->{sub}->();
}
 
package main;
 
sub mySub {
my $mySubGuard = $sClass->new(sub { print "shouldDestroyFirst\n" });
 
# Do something - any no-op will do.
undef;
 
# Comment out this line and it works
undef $mySubGuard;
 
# Or uncomment the next undef line and it works. Any statement(s) or
# no-ops will do but we test the return value of mySub to make sure it
# doesn't return a reference, so undef...
# undef;
}
 
{
my $scopeGuard = $sClass->new(sub { print "shouldDestroyLast\n" });
 
# Check that mySub returns undef to ensure the reference *did* go out
# of scope
printf "mySub returned a %sdefined value\n",
defined mySub() ? "" : "un";
}
 
print "done\n";

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.