Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

try catch getting ignored by SIG DIE subroutine

by dkhosla1 (Sexton)
on Feb 25, 2018 at 07:08 UTC ( [id://1209918]=perlquestion: print w/replies, xml ) Need Help??

dkhosla1 has asked for the wisdom of the Perl Monks concerning the following question:

Appreciate insight on why if I have a defined SIG{__DIE__}, it is overriding a try/catch block? If I comment out the SIG{__DIE__} code, the 'catch' works; but otherwise it does not.

use strict; #use diagnostics; use JSON qw(decode_json encode_json); use Try::Tiny; use warnings; $SIG{__DIE__} = sub { my $item = shift; my $lasterr = ""; $lasterr = $@ if (defined($@)); chomp ($item); print STDERR "DIE ERROR: $item : $lasterr\n"; local $! = 2; exit $!; }; # end SIG_DIE sub write_mets { my $collref; # Read first line of test file my $file = "data1"; open (my $f, "<", $file) || die "Error opening: $!"; my $json1 = <$f>; close ($f); try { $collref = decode_json ($json1); print STDOUT "Read file $file\n"; } catch { print STDERR "Error decoding data from $file $@ \n"; }; } write_mets(); 1;

As run above, I get the following: (data1 has some bad json)

$ perl S2.pl DIE ERROR: garbage after JSON object, at character offset 7413 (before + "n\n") at S2.pl line 30. :

If I comment out the SIG_DIE code, I get

$ perl S2.pl Error decoding data from data1
Noticed also that $@ also does not have a value in this case. The detailed error is missing. I really need the SIG_DIE functionality for the rest of the code, just not have it get int he way in the try/catch section :-). After several hours of googling and trying, have given up. Monks! Help! Thanks!

Replies are listed 'Best First'.
Re: try catch getting ignored by SIG DIE subroutine (updated!)
by haukex (Archbishop) on Feb 25, 2018 at 08:27 UTC
    Appreciate insight on why if I have a defined SIG{__DIE__}, it is overriding a try/catch block?

    It doesn't override it - you are, by calling exit inside the handler. You don't need to do that, since $SIG{__DIE__} gets called when the program is dieing anyway. If I remove the exit and leave the rest of the code unchanged, I get the output:

    DIE ERROR: malformed JSON string, neither array, object, number, strin +g or atom, at character offset 0 (before "(end of string)") at 120991 +8.pl line 26. : Error decoding data from data1

    Which is hopefully getting a bit closer to what you want. As for the special variables, I think you've got a couple of things mixed up:

    • $! doesn't have anything to do with the exit status of the program (and especially not when you set it with local). Setting $! with local won't work, as the changes will be local to the handler only.
    • Try::Tiny gives you the error in $_, not $@.
    • Checking $@ inside of the handler doesn't make sense to me - you aren't supposed to rely on the values of any of the Error Variables except for immediately after an operation that is documented to set them fails.
    I really need the SIG_DIE functionality for the rest of the code, just not have it get int he way in the try/catch section

    If you really don't want the handler to be called inside a try/catch, you can temporarily disable it like Athanasius suggested, although personally I would do it with local, that is: try { local $SIG{__DIE__} = 'DEFAULT'; ... (no other changes needed). It's also possible to use $^S, like Corion suggested, to customize the behavior of the handler based on whether it's inside an eval or not, for example by sticking die @_ if $^S; at the very beginning - although make sure to read all of the documentation, for example %SIG strongly cautions against using $^S!

    Now, as for the custom exit code, it seems that unfortunately you can't set $? from within a $SIG{__DIE__} handler - although you can set it from inside an END block. Although I don't consider this the most elegant solution, something like this might be closer to what you want:

    { # block to hide $_exitcode from rest of program my $_exitcode; $SIG{__DIE__} = sub { die @_ if $^S; # don't run this handler from inside "eval" my $err = shift; chomp($err); print STDERR "DIE ERROR: $err\n"; $_exitcode = 2; }; END { $?=$_exitcode if defined $_exitcode } }

    In any case, all of this seems like you're doing some relatively complicated error handling - I suggest you read all of the documentation I've linked above. Also, I would encourage considering if such complex error handling is necessary in the first place - if you could give some more background, like why you need all this (especially the custom exit code), and how you expect your program to behave exactly in the face of different errors, what kind of errors you are expecting, etc., perhaps we can suggest a better overall solution.

    Update: As ikegami pointed out, $! is in fact related to the exit code, I was wrong above. And as it turns out, you can set it from inside the handler, which significantly simplifies the above code:

    $SIG{__DIE__} = sub { die @_ if $^S; # don't run this handler from inside "eval" my $err = shift; chomp($err); print STDERR "DIE ERROR: $err\n"; $! = 2; };

    Although if, as you said in the reply, you want to temporarily disable the handler with local $SIG{__DIE__} = 'DEFAULT';, you can remove the die @_ if $^S; (I think that's probably better). If I use this handler in your original code (and I fix the $@/$_ mixup), I get the following output, without the "DIE ERROR:" message, which I think is what you wanted?

    Error decoding data from data1 malformed JSON string, neither array, o +bject, number, string or atom, at character offset 0 (before "(end of + string)") at 1209918.pl line 25.

      $! doesn't have anything to do with the exit status of the program

      An uncaught exception results in exit($! || $? >> 8 || 255).

      Thanks for the insight and the various options:

      - Thanks for the $@ vs. $_ in Try::Tiny. It was right there in first example in the docs and somehow missed it!

      - The __DIE__ & __WARN__ handling is a lot more complex in the real code. This is in a common library used bu a lot of apps. This code snippet was with some minimal lines. 'die' is being called all over the code to fill some special logs with formatted messages and some other processing, code setting etc. before exiting. Maybe, as recommended, I can clean it up a little and make it simpler. Will review the docs some more. I had struggled with the exit ($?) from __DIE__ vs. regular exit in the END block. Thanks for the clarification. The $_exitcode type solution will help.

      - Looks like the example from Athanasius is what I can safely try for now with your 'local' enhancement. Seems cleaner.

Re: try catch getting ignored by SIG DIE subroutine
by Athanasius (Archbishop) on Feb 25, 2018 at 07:44 UTC

    Hello dkhosla1,

    See also the entry for %SIG in perlvar#General-Variables.

    I really need the SIG_DIE functionality for the rest of the code, just not have it get int he way in the try/catch section :-).

    You can disable the signal handler for just the section(s) of code containing try/catch:

    use strict; use warnings; use Try::Tiny; $SIG{__DIE__} = sub { chomp(my $item = shift); print STDERR "DIE ERROR: $item\n"; exit 2; }; # ... code here ... my $last_sig_die = $SIG{__DIE__}; # Save current setting $SIG{__DIE__} = 'DEFAULT'; try { die 'Dying'; } catch { print STDERR $_; # Note: $_, not $@ }; $SIG{__DIE__} = $last_sig_die; # Restore setting die 'Really dying';

    Output:

    17:42 >perl 1871_SoPW.pl Dying at 1871_SoPW.pl line 19. DIE ERROR: Really dying at 1871_SoPW.pl line 28. 17:42 >

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Thanks Athanasius. This should help. Will try with the 'local' enhancement suggested by haukex.
Re: try catch getting ignored by SIG DIE subroutine
by Corion (Patriarch) on Feb 25, 2018 at 07:21 UTC

    See the bottom of die where the (bad) interaction of die with signal handlers is discussed and $^S is mentioned as the place to check whether you're within an eval block or not.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1209918]
Approved by Athanasius
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (3)
As of 2024-09-07 14:18 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?
    erzuuli‥ 🛈The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.