Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@pmorch
Created July 11, 2012 07:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pmorch/3088551 to your computer and use it in GitHub Desktop.
Save pmorch/3088551 to your computer and use it in GitHub Desktop.
perl: DESTROY called in unexpected sequence
#!/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";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment