Need an end of lexical scope action which can die normally

Posted by Schwern on Stack Overflow See other posts from Stack Overflow or by Schwern
Published on 2011-01-05T23:49:50Z Indexed on 2011/01/05 23:54 UTC
Read the original article Hit count: 351

Filed under:
|
|

I need the ability to add actions to the end of a lexical block where the action might die. And I need the exception to be thrown normally and be able to be caught normally.

Unfortunately, Perl special cases exceptions during DESTROY both by adding "(in cleanup)" to the message and making them untrappable. For example:

{
    package Guard;

    use strict;
    use warnings;

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

    sub DESTROY {
        my $self = shift;
        $self->();
    }
}

use Test::More tests => 2;

my $guard_triggered = 0;

ok !eval {
    my $guard = Guard->new(
#line 24
        sub {
            $guard_triggered++;
            die "En guarde!"
        }
    );
    1;
}, "the guard died";

is $@, "En guarde! at $@ line 24\n",    "with the right error message";
is $guard_triggered, 1,                 "the guard worked";

I want that to pass. Currently the exception is totally swallowed by the eval.

This is for Test::Builder2, so I cannot use anything but pure Perl.

The underlying issue is I have code like this:

{
    $self->setup;

    $user_code->();

    $self->cleanup;
}

That cleanup must happen even if the $user_code dies, else $self gets into a weird state. So I did this:

{
    $self->setup;

    my $guard = Guard->new(sub { $self->cleanup });

    $user_code->();
}

The complexity comes because the cleanup runs arbitrary user code and it is a use case where that code will die. I expect that exception to be trappable and unaltered by the guard.

I'm avoiding wrapping everything in eval blocks because of the way that alters the stack.

© Stack Overflow or respective owner

Related posts about perl

Related posts about exception