Hi,
A co-worker and I recently figured out a problem with the differences in
signal handling in between perl 5.6.x and perl 5.8.x, that has plagued
us for about 9 months, but which I did not have time to resolve sooner.
The problem is that code which looks like this:
my $alarm = 0;
eval {
local $SIG{ALRM} = sub { $alarm=1 };
alarm(3);
$dbh = DBI->connect( "dbi:Oracle:$dbn", $usr, $pwd
,{ AutoCommit=>$self->auto_commit()
,RaiseError=>1
,PrintError=>$self->print_err()
} );
alarm(0);
};
alarm(0);
if ($@)....
if ( $alarm ).... etc
Does not work the same way in perl 5.6.x and 5.8.x.
This is because 5.8.x introduced the use of the SA_RESTART flag
in the call to the underlying sigaction() function. In the above
example, the DBI->connect() through the oracle libraries eventually
call's the connect() system call. In perl 5.8.x this hangs for
a VERY long time if the system which hosts the database is down. The
signal handler is called, but connect() function is restarted.
I have written a module the uses POSIX:sigaction() directly as
suggested in the perlvar man page. (Note that code in the the perlvar
page does not work!). The working name of this module is currently
SignalHandler. This module is almost as convenient to use as the above:
#timeout a system call:
use POSIX ':signal_h' ;
use SignalHandler qw( set_handler );
eval {
local $SIG{ALRM};
set_handler( 'mypackage::mysubname' ,SIGALRM );
alarm(2)
#... do something you want to timeout for instance:
$dbh = DBI->connect( "dbi:Oracle:$dbn", $usr, $pwd
,{ AutoCommit=>$self->auto_commit()
,RaiseError=>1
,PrintError=>$self->print_err()
} );
alarm(0);
};
#perl clears the handler here... because of the local dec above
alarm(0);
if ( $@ ) ...
So, I have two questions:
1) Does anyone think such a module would be of general interest?
2) What would a good (Name/Namespace) for this module?
Module source follows. Note that it is still in a proof stage and would
be much more thoroughly documented before up load.
I'm also thinking of adding a timeout() function which would take a sub
ref and a timeout value, and execute the sub with a SIGALRM timeout
wrapped around it. I still need to test for the availability of
sigaction() with %Config (although I could do that in the Makefile.PL),
(and perhaps), provide a signal() based implementation if sigaction() is
not found.
################ code begins here ##############
package SignalHandler;
=head1 NAME
SignalHandler
=head1 SYNOPSYS
use POSIX ':signal_h' ;
use SignalHandler qw( set_handler reset_action );
my $oldaction = set_handler( 'mypackage::mysubname' ,SIGINT );
... do stuff non-interupt able
reset_action( $oldaction ,SIGINT );
or
#timeout a system call:
use POSIX ':signal_h' ;
use SignalHandler qw( set_handler );
eval {
local $SIG{ALRM};
set_handler( 'mypackage::mysubname' ,SIGALRM );
alarm(2)
... do something you want to timeout
alarm(0);
};
#perl clears the handler here...
alarm(0);
if ( $@ ) ...
or
use POSIX ':signal_h' ;
use SignalHandler qw( set_handler reset_action );
my $oldaction;
eval {
$oldaction = set_handler( 'mypackage::mysubname' ,SIGALRM );
alarm(2)
... do something you want to timeout
alarm(0);
};
alarm(0);
reset_action( $oldaction ,SIGALRM );
if ( $@ ) ...
=head1 DESCRIPTION
TODO...
=cut
use strict;
use warnings;
use POSIX ':signal_h' ;
require Exporter;
our ( @ISA ,@EXPORT_OK );
@ISA = qw( Exporter );
@EXPORT_OK = qw( set_handler set_action reset_action sigset sigact );
sub set_handler( $$ )
{
my ( $handler ,$sig ,$flags ) = @_;
my $sigset = sigset( $sig );
my $act = sigact( $handler ,$sig ,$flags );
return set_action( $act ,$sig );
}
sub set_action($$)
{
my ( $action ,$sig ) = @_;
my $oact = POSIX::SigAction->new();
sigaction( $sig ,$action ,$oact );
return $oact;
}
sub reset_action( $$ )
{
my ( $oldaction ,$sig ) = @_;
sigaction( $sig ,$oldaction );
}
sub sigset
{
my $sig = shift @_;
return $sig if UNIVERSAL::isa( $sig ,'POSIX::SigSet' );
return POSIX::SigSet->new( $sig );
}
sub sigact($$;$)
{
my ( $handler ,$sigset ,$flags ) = @_;
my $sact = POSIX::SigAction->new( $handler ,$sigset ,$flags );
}
1;