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

Within my program I have the following construct:
eval { local $SIG{'ALRM'} = sub { $STATE{'EVENT'} = 1; die('ALRM EVENT'); }; local $SIG{'CHLD'} = sub { $STATE{'EVENT'} = 1; die('CHLD EVENT'); }; if (! $STATE{'EVENT'}) { $cmd = <STDIN>; } };
It's purpose is to gather user input in an interruptable manner. Theoretically, the eval block should catch the die()s. However, twice my program has terminated due to the die('CHLD EVENT'). I can only guess that it's due to the receipt of multiple signals simultaneously. I cannot reproduce the behaviour with a test program, and it has only occurred twice during several tens of hours of operation.
I thought of wrapping the eval block inside another eval block as a workaround.
Is this a known problem? Are there more appropriate alternatives to my workaround?
I am using 5.8.2 Perl under Cygwin.

Replies are listed 'Best First'.
Re: Eval not catching Dies (local)
by tye (Sage) on Dec 08, 2003 at 17:34 UTC

    I'd first check that dieing out of an eval still gives the two locals a chance to do their "undo" magic. Even if it does, this "undo" magic may well be happening outside of the eval's protection against die, giving you a race condition.

    You could probably even write some test code such that the local undo causes something to be destroyed which dies which should demonstrate whether you are still inside of eval's protection.

    sub DESTROY { warn "Destroying $_[0][0]...\n"; die "Die from $_[0][0]\ +n" } $global= "before"; eval {{ local $global= bless ["global"]; die "Die inside eval.\n" unless @ARGV; }}; warn "global($global) eval($@)\n";
    which, if @ARGV is empty, produces:
    Destroying global... global(before) eval(Die inside eval. (in cleanup) Die from global )

    which seems to indicate that my version of Perl is handling this case correctly.

                    - tye
Re: Eval not catching Dies
by Coruscate (Sexton) on Dec 08, 2003 at 17:44 UTC

    Are you trying to catch the die() after the eval()? If so, my best guess is the fact that the perldoc for alarm says that the die() in the sig handler must terminate with a newline. So you just might need this instead (just a guess, I really don't know if this will help at all). Note that I took out the state handler. You really shouldn't need that unless you need to maintain state further.

    my $cmd; eval { local $SIG{'ALRM'} = sub { die "DIED\n" }; local $SIG{'CHLD'} = sub { die "DIED\n" }; $cmd = <STDIN>; }; if ($@) { die $@ eq "DIED\n" ? "We died from a sig handler\n" : "Other death: $@\n"; }

      > Are you trying to catch the die() after the eval{}?
      No. The purpose of the die() is to 'terminate' the read from STDIN. Outside the eval, I don't care if it died. I just check to see if the user input anything for $cmd, and then I process that.
      > perldoc for alarm says that the die() in the sig handler must terminate with a newline
      Okay. I'll add one. However, that begs the question of "Why does it have to have a newline?" I tested alarm handlers before without newlines and they work. This looks like an artifact to me.
        Well, I figured out the 'newline' issue. Adding a newline at the end of the die() text suppresses the addition of 'at xxx line #.'. The newline comment in the alarm code example is given because of the 'eq "alarm\n" conditional further down. Without the newline, the same conditional can be realized using '=~ /^alarm/'. Therefore, the newline is not germane to my problem. (Although, I did add one.)
Re: Eval not catching Dies
by ambrus (Abbot) on Dec 09, 2003 at 07:15 UTC

    I belive you're tricked by perl 5.8's delayed signal mechanism. The signal actions are only carried out between execing perl opcodes. The <STDIN> reading restarts after the signal, and your $SIG{} functions are only executed after the readline's finished.

    (But I'm not quite sure.)

Re: Eval not catching Dies
by Abigail-II (Bishop) on Dec 08, 2003 at 16:13 UTC
    Is this a known problem?

    Not that I know of.

    Abigail