Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl die() call mysteriously not dying

Tags:

die

perl

eval

I was able to get to this short code after doing some serious debugging at work with a very obscure bug in the project. A die call that was not dying.

The issue happens only when calling script.pl. If you call Class_A directly, then the die call will be successful.

We need three files:

File 1: script.pl

use strict;
use warnings;
use lib '.';
use Class_A;

# This should not execute. Class_A should die at loading time
print "We shouldn't get here. Class_A shoud not load and die.\n";

File 2: Class_A.pm

package Class_A;
use strict;
use warnings;
use Class_B;

# This code SHOULD die:
my $p = Class_B->new;
$p->do_something->die_now;


1;

File 3: Class_B.pm

package Class_B;
use strict;
use warnings;

sub new {
    my $class = shift;
    bless {}, $class;
}

sub do_something {
    my $self = shift;
}

sub die_now {
    die "No soup for you!";
}

sub DESTROY {
    eval {
        1;
    };
}

1;

Notice the chained call at Class_A.pm line 8? Well, if you unchain it, then the code dies successfully. :-|

# This works. There should be no difference.
$p->do_something;
$p->die_now;

And, the final surprise was to find out that just by removing the eval call at Class_B.pm line 19, then things work as expected and the script dies.

I had the opportunity to test this in Perl 5.22.2, Perl 5.26.1 and Perl 5.32.0. For another wonderful surprise, this issue doesn't happen on 5.32.0 only.

Seriously, WT*? Any thoughts on what is going on here?

like image 952
Francisco Zarabozo Avatar asked Aug 14 '20 04:08

Francisco Zarabozo


People also ask

How to call die from an empty list?

If LIST was empty or made an empty string, and $@ is also empty, then the string "Died" is used. You can also call die with a reference argument, and if this is trapped within an eval, $@ contains that reference.

How do you handle uncaught exceptions in Perl?

If the exception is outside of all enclosing eval s, then the uncaught exception is printed to STDERR and perl exits with an exit code indicating failure. If you need to exit the process with a specific exit code, see exit. Most of the time, die is called with a string to use as the exception.

How to use die () method in eval ()?

If LIST was empty or made an empty string, and $@ is also empty, then the string "Died" is used. You can also call die with a reference argument, and if this is trapped within an eval, $@ contains that reference. This permits more elaborate exception handling using objects that maintain arbitrary state about the exception.


1 Answers

The code you posted doesn't exhibit the problem since 5.28.

The problem relates to $@ getting clobbered (using eval { }) during the unwind that occurs due to an exception. Apparently, unsetting $@ fools Perl into thinking no exception occurred.

According to perl528delta, $@ is now set after everything is destroyed, which prevents the destructor from clobbering $@. You can also prevent the destructor from clobbering $@ by adding local $@; (e.g. to support older versions of Perl).

like image 130
ikegami Avatar answered Oct 13 '22 11:10

ikegami