When I wrote about exception handling in Perl I mentioned briefly that DESTROY() methods should localize $? too.
Try this script:
#!/usr/bin/perl
package Foo;
sub new {
my $class = shift;
open my $fh, "-|", "cat /dev/zero";
return bless { fh => $fh }, $class;
}
sub DESTROY {
my $self = shift;
close $self->{fh}
}
package main;
my $foo = Foo->new();
exit 42;
You would expect it to return with status code 42, but when I run it the status code is 13. Localizing $? in the DESTROY() method gives the expected 42 status code.
The most common usage of $? is explained in the perlvar manual page:
The status returned by the last pipe close, backtick (““”) command, successful call to wait() or waitpid(), or from the system() operator.
And the above destructor makes a ‘pipe close’ changing the value of $?. But the perlvar manual also mentions an secondary usage of $?:
Inside an “END” subroutine $? contains the value that is going to be given to “exit()”. You can modify $? in an “END” subroutine to change the exit status of your program.
But this doesn’t just holds for END block, it is true for any code run after the exit() invokation – including destructors.
And then to a mystery. Given the above Foo package it isn’t very surprising that this script have the status code 13:
use Foo;
my $foo = Foo->new();
But why does the following change make the script have the status code 0?
use Foo;
my $foo = Foo->new();
$? = 42;