use strict; use warnings ; $| = 1 ; my $outr = 0 ; # count of ALRMs seen by "outer" handler my $rcvd = 0 ; # count of ALRMs seen by "wrap_alrm" handler my $wrap = 0 ; # count of ALRMs seen by "wrap_alrm" handler *outside* eval my $dies = 0 ; # count of ALRMs seen by "wrap_alrm" signal *inside* eval my $eval = 0 ; # Flag set while in eval {} in "wrap_alrm" my %handlers = () ; sub wrap_alrm { my $died = $dies ; $eval = 1 ; # NB: there is a race here eval { my $old = $SIG{ALRM} || 'DEFAULT' ; $SIG{ALRM} = sub { $rcvd++ ; if ($eval) { $dies++ ; die ("SIG ALRM $rcvd\n") ; } else { $wrap++ ; } ; } ; $handlers{$SIG{ALRM}} = 'wrap_alrm' ; sleep 1 ; $SIG{ALRM} = $old ; } ; $eval = 0 ; # And a race here, too return $dies - $died ; } my $pid = fork; if ($pid) { $SIG{ALRM} = sub { $outr++ ; } ; $handlers{$SIG{ALRM}} = 'outr_alrm' ; my $timeout = time + 10 ; print "+++ parent entering wrap_sigs loop\n" ; while (time < $timeout) { print "\$SIG{ALRM}=", $handlers{$SIG{ALRM}}, " enter wrap_alrm\n" ; my $died = wrap_alrm() ; print "\$SIG{ALRM}=", $handlers{$SIG{ALRM}}, " exit wrap_alrm ($died) ", "\$outr=$outr; \$rcvd=$rcvd; \$dies=$dies; \$wrap=$wrap\n" ; sleep 1 ; print "\$SIG{ALRM}=", $handlers{$SIG{ALRM}}, " after sleep ", "\$outr=$outr; \$rcvd=$rcvd; \$dies=$dies; \$wrap=$wrap\n" ; } ; print "parent survived, killing child\n" ; kill TERM => $pid ; } else { sleep 6 ; print "+++ child starting signal storm\n" ; my $timeout = time + 2 ; while(time < $timeout) { kill ALRM => getppid ; } ; } ;