in reply to Re: Setting signal handlers considered unsafe?
in thread Setting signal handlers considered unsafe?

I'm pleased to see that you have reported this as a bug:

  #60360: local $SIG{FOO} = sub {...}; sets signal handler to SIG_DFL

Looking at the strace output:

  rt_sigprocmask(SIG_BLOCK, ALRM, [], 8) = 0
  rt_sigaction(SIGALRM, {SIG_DFL}, {0x80a3330, [], 0}, 8) = 0
  rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
it appears that the SIG_BLOCK call of rt_sigprocmask is storing the mask state, and that the SIG_SETMASK call is restoring that -- certainly that's what one would hope for !

While one waits for a fix, the work-around seems to be to wrap  local $SIG{FOO} = ... in a  SIG_BLOCK/SIG_SETMASK pair -- as suggested by brother gnosek in 721812, above:

use POSIX qw(:signal_h) ; my $sigset = POSIX::SigSet->new ; my $blockset = POSIX::SigSet->new( SIGALRM ) ; sigprocmask(SIG_BLOCK, $blockset, $sigset ); local $SIG{ALRM} = sub .... ; sigprocmask(SIG_SETMASK, $sigset );

The code below tests with and without the extra masking. With the extra masking I haven't seen it fail. Without the extra masking it fails almost instantly with a "TWIDDLE" of 1, but seems to last longer for larger "TWIDDLE".

What I find remarkable is how quickly this fails when the extra masking is disabled. SIG_DFL is set only briefly. On the assumption that the ALRM signals are essentially random wrt the main process, I'd expect the time to fail to be more or less related to the time that SIG_DFL is set.

I may be underestimating how long the various system calls take, compared to the small amount of Perl code. Or there may be some part of the system calls causing some synchronisation between the processes, or a greater likelihood of collecting an ALRM at the SIG_SETMASK call...

The $TWIDDLE argument increases the time spent with the "temporary" ALRM handler set. Increasing that value does appear to increase how long it takes before the SIG_DFL handler is invoked (in the absense of the extra masking) and the program comes to a sudden Alarm clock halt.

I have not seen the error:

  Unable to create sub named "" at ....
reported in 721812, but brother gnosek is running 5.8.8 while I am running 5.10.0. I wonder if it's to do with creating a very large number of anonymous subroutines ?

use warnings ; use strict ; my $MASK = @ARGV ? shift(@ARGV) : 1 ; my $TWIDDLE = @ARGV ? shift(@ARGV) : 1 ; $| = 1 ; use POSIX qw(:signal_h) ; use Time::HiRes qw(time) ; my $main_alrm = 0 ; my $temp_alrm = 0 ; $SIG{ALRM} = sub { $main_alrm++ ; }; sub set_temp_alrm { my $sigset = POSIX::SigSet->new ; my $blockset = POSIX::SigSet->new( SIGALRM ) ; sigprocmask(SIG_BLOCK, $blockset, $sigset) if $MASK ; local $SIG{ALRM} = sub { $temp_alrm++ ; } ; sigprocmask(SIG_SETMASK, $sigset) if $MASK ; my $t = $TWIDDLE ; while ($t) { $t-- ; } ; } ; if (fork) { print "parent started MASK=$MASK TWIDDLE=$TWIDDLE\n" ; my $start = time ; my $next = $start ; while (1) { set_temp_alrm() ; my $now = time ; if ($now > $next) { printf "main:%6d temp:%6d after:%6.1fs\n", $main_alrm, $temp_alrm, $now-$start ; $next = $now + 1 ; } ; } ; } else { print "child started\n" ; sleep 2 ; print "ALRM storm started\n"; 1 while kill ALRM => getppid; } ;

Replies are listed 'Best First'.
Re^3: Setting signal handlers considered unsafe?
by ig (Vicar) on Nov 11, 2008 at 11:19 UTC
    I'm pleased to see that you have reported this as a bug:

    If perl were new, I might argue that local should not set variables when localizing (to undef or anything else and regardless of whether there is an initializer), as initializers are available to set the value and including this feature in local precludes not changing the value. But perl is not new and changing local now would no doubt be problematic.

    After reading the further posts in this thread, I am inclined to the view that the design of local is less than ideal but that the only bug is in the documentation. In which case I guess I should submit a patch to the docs to go along with my bug report. I am thinking perlsub and perlipc should probably both be changed. Are there other sections that need attention?

    Maybe what is needed is a new operator, very much like local except that it does not change the value of the variable. It's description in perlsub would be exactly as for local except for the definition of what happens if there is no initializer, which might read as follows for the new operator:

    If no initializer is given for a particular variable its value is not changed if it already exists or, if it does not already exist, its value is set to undef.

    This would leave local as is, so no existing scripts would be broken, and it would provide a new means of managing signal handlers, and anything else where setting the variable to undef, even briefly, is undesirable.

    But this wouldn't be a bug, this would be a feature request.

    Update: just as I was about to submit patches to perlsub and perlipc, I received notice that a patch has been applied that skips setting undef if there is an initializer. I hope it doesn't upset anyone and get taken out.