NAME
Signals::XSIG - install multiple signal handlers through %XSIG
VERSION
Version 0.13
SYNOPSIS
use Signals::XSIG q{:all};
# drop-in replacement for regular signal handling through %SIG
$SIG{TERM} = \&my_sigterm_handler;
$SIG{USR1} = sub { ... };
$SIG{PIPE} = 'DEFAULT';
# %XSIG interface to installing multiple signal handlers
$SIG{TERM} = \&handle_sigterm; # same as $XSIG{TERM}[0] = ...
$XSIG{TERM}[3] = \&posthandle_sigterm;
$XSIG{TERM}[-1] = \&prehandle_sigterm;
# SIGTERM calls prehandle_sigterm, handle_sigterm, posthandle_sigterm
# in that order.
# array operations allowed on @{$XSIG{signal}}
push @{$XSIG{__WARN__}}, \&log_warnings;
unshift @{$XSIG{__WARN__}}, \&remotely_log_warnings;
warn "This warning invokes both handlers";
shift @{$XSIG{__WARN__}};
warn "This warning only invokes the 'log_warnings' handler";
DESCRIPTION
Perl provides the magic global hash variable %SIG
to make it easy to trap and handle signals (see "%SIG" in perlvar and perlipc). The hash-of-lists variable %XSIG
provided by this module has a similar interface for setting an arbitrary number of handlers on any signal.
There are at least a couple of use cases for this module:
You have written a module that raises signals and makes use of signal handlers, but you don't want to preclude the end-user of your module from doing their own handling of that signal. The solution is to install your own signal handler into a "non-default" index. Now your module's end-user can set and unset
$SIG{signal}
as much as he or she would like. When the signal is trapped, both your module's signal handler and the end-user's signal handler (if any) will be invoked.package My::Module::With::USR1::Handler; use Signals::XSIG; sub import { ... # use $XSIG{USR1}, not $SIG{USR1}, in case the user of # this module also wants to install a SIGUSR1 handler. # Execute our handler BEFORE any user's handler. $XSIG{'USR1'}[-1] = \&My_USR1_handler; ... } sub My_USR1_handler { ... } sub My_sub_that_raises_SIGUSR1 { ... } ... 1;
Now the user of your module can still install their own
SIGUSR1
handler through$SIG{USR1}
without interfering with your owmSIGUSR1
handler.You have multiple "layers" of signal handlers that you want to enable and disable at will. For example, you may want to enable some handlers to write logging information about signals received.
use Signals::XSIG; # log all warning messages $XSIG{__WARN__}[1] = \&log_messages; do_some_stuff(); # now enable extra logging -- warn will invoke both functions now $XSIG{__WARN__}[2] = \&log_messages_with_authority; do_some_more_stuff(); # done with that block. disable extra layer of logging $XSIG{__WARN__}[2] = undef; # continue, &log_warnings will still be called at next warn statement
%XSIG
Extended signal handling is provided by making assignments to and performing other operations on the hash-of-lists %XSIG
, which is imported into the calling namespace by default.
A signal handler
is one of the following or any scalar variable that contains one of the following:
DEFAULT
IGNORE
undef
''
unqualified_sub_name # qualified to main::unqualified_sub_name
qualified::sub_name
\&subroutine_ref
sub { anonymous sub }
*unqualified_glob # qualified to *CallingPackage::unqualified_glob
*qualified::glob
(the last two handler specifications cannot be used with Perl 5.8 due to a limitation with assigning globs to tied hashes. See "BUGS AND LIMITATIONS").
There are several ways to enable additional handlers on a signal.
- $XSIG{signal} = handler
-
Sets a single signal handler for the given signal.
- $XSIG{signal}[0] = handler
-
Identical behavior to the conventional
$SIG{signal} = handler
expression. Installs the specified signal handler as the "main" signal handler. If you are using this module because you don't want your signal handlers to trample on the signal handlers of your users, then you generally don't want to use this expression. - $XSIG{signal}[n] = handler for n > 0
- $XSIG{signal}[-n] = handler for -n < 0
-
Installs the given signal handler at the specified indicies. When multiple signal handlers are installed and a signal is trapped, the signal handlers are invoked in order from lowest indexed to highest indexed.
For example, this code:
$XSIG{USR1}[-2] = sub { print "J" }; $XSIG{USR1}[-1] = sub { print "A" }; $XSIG{USR1}[1] = sub { print "H" }; $SIG{USR1} = sub { print "P" }; # $SIG{USR1} is alias for $XSIG{USR1}[0] kill 'USR1', $$;
should output the string
JAPH
. If a "main" signal handler is installed, then use this expression with a negative index to register a handler to run before the main handler, and with a positive index for a handler to run after the main handler.A signal handler at a specific slot can be removed by assigining
undef
or''
(the empty string) to that slot.$XSIG{USR1}[1] = undef;
- $XSIG{signal} = [handler1, handler2, ...]
- @{$XSIG{signal}} = (handler1, handler2, ...)
-
Installs multiple handlers for a signal in a single expression. Equivalent to
$XSIG{signal} = []; # clear all signal handlers $XSIG{signal}[0] = handler1; $XSIG{signal}[1] = handler2; ...
All the handlers for a signal can be uninstalled with a single expression like
$XSIG{signal} = []; @{XSIG{signal}} = ();
- push @{$XSIG{signal}}, handler1, handler2, ...
-
Installs additional signal handlers to be invoked after all currently installed signal handlers. There is a corresponding
pop
operation, but it cannot be used to remove the main handler or any prior handlers.$XSIG{USR1} = []; $XSIG{USR1}[-1] = \&prehandler; $XSIG{USR1}[0] = \&main_handler; $XSIG{USR1}[1] = \&posthandler; push @{$XSIG{USR1}}, \&another_posthandler; pop @{$XSIG{USR1}}; # removes \&another_posthandler pop @{$XSIG{USR1}}; # removes \&posthandler pop @{$XSIG{USR1}}; # no effect - pop doesn't remove index <= 0
- unshift @{$XSIG{signal}}, handler1, handler2, ...
-
Analagous to
push
, installs additional signal handlers to be invoked before all currently installed signal handlers. The correspondingshift
operation cannot be used to remove the main handler or any later handlers.$XSIG{USR1} = [ $h1, $h2, $h3, $h4 ]; $XSIG{USR1}[-1] = $j1; $XSIG{USR1}[-3] = $j3; unshift @{$XSIG{USR1}}, $j4; # installed at $XSIG{USR1}[-4] shift @{$XSIG{USR1}}; # removes $j4 shift @{$XSIG{USR1}}; # removes $j3 shift @{$XSIG{USR1}}; # removes $XSIG{USR1}[-2], which is undef shift @{$XSIG{USR1}}; # removes $j1 shift @{$XSIG{USR1}}; # no effect - shift doesn't remove index >= 0
OVERRIDING DEFAULT SIGNAL BEHAVIOR
Signals::XSIG
provides two ways that the 'DEFAULT' signal behavior (that is, the behavior of a trapped signal when one or more of its signal handlers is set to 'DEFAULT'
, not the behavior when a signal does not have a signal handler set) can be overridden for a specific signal.
define a
Signals::XSIG::Default::default_<SIG>
functionsub Signals::XSIG::Default::default_QUIT { print "Hello world.\n"; } $SIG{QUIT} = 'DEFAULT'; kill 'QUIT', $$;
set a handler in
%Signals::XSIG::DEFAULT_BEHAVIOR
$Signals::XSIG::DEFAULT_BEHAVIOR{USR1} = sub { print "dy!" } $XSIG{'USR1'} = [ sub {print "How"}, 'DEFAULT', sub{print$/} ]; kill 'USR1', $$; # "Howdy!\n"
Note again that the overridden 'DEFAULT' behavior will only be used for signals where a handler has been explicitly set to 'DEFAULT'
, and not for signals that do not have any signal handler installed. So
$SIG{USR1} = 'DEFAULT'; kill 'USR1', $$;
will use the overridden default behavior, but
$XSIG{USR1} = []; kill 'USR1', $$;
will not.
Also note that in any chain of signal handler calls, the 'DEFAULT' signal handler will be called at most once. So for example this code
my $x = 0;
$Signals::XSIG::DEFAULT_BEHAVIOR{USR2} = sub { $x++ };
$XSIG{USR2} = [ 'DEFAULT', sub {$x=11}, 'DEFAULT', 'DEFAULT' ];
kill 'USR2', $$;
print $x;
will output 11, not 13. This is DWIM.
EXPORT
The %XSIG
extended signal handler hash is exported into the calling namespace by default.
FUNCTIONS
None
OTHER NOTES
DEFAULT signal handler
If the main handler for a signal ($XSIG{signal}[0]
) is set to DEFAULT
, that handler will be ignored if there are any other handlers installed for that signal. This is DWIM.
For example, this will invoke the default behavior for SIGUSR1:
$SIG{USR1} = 'DEFAULT';
kill 'USR1', $$;
but this will not
$SIG{USR1} = 'DEFAULT';
$XSIG{USR1}[1] = \&do_something_else;
kill 'USR1', $$;
This will also invoke the default behavior for SIGTERM (probably terminating the program) since it is not the main handler that is the DEFAULT
handler:
$SIG{TERM} = \&trap_sigterm;
$XSIG{TERM}[-1] = 'DEFAULT';
kill 'TERM', $$;
If the DEFAULT
handler is installed more than once, the default behavior for that signal will only be invoked once when that signal is trapped.
AUTHOR
Marty O'Brien, <mob at cpan.org>
BUGS AND LIMITATIONS
- Avoid
local %SIG
-
This module converts
%SIG
into a tied hash. As documented in the perltie "BUGS" section,local
izing a tied hash will cause the old data not to be restored when the local version of the hash goes out of scope. Avoid doing this:{ local %SIG; ... }
or using modules and functions which localize
%SIG
(fortunately, there are not that many examples of code that use this construction [http://www.google.com/codesearch?hl=en&lr=&q=%22local+%25SIG%22+lang:perl&sbtn=Search]).Should you identify a code block that localizes
%SIG
and you can't/don't want to avoid using it, the workaround is to save and restore%SIG
at the end of the local scope:use Signals::XSIG; ... my %temp = %SIG; function_call_or_block_that_localizes_SIG(); %SIG = %temp;
In addition, the behavior of the tied
%SIG
while it islocal
'ized is different in different versions of Perl, and all of the features ofSignals::XSIG
might or might not work while a local copy of%SIG
is in use.Note that it is perfectly fine to
local
ize an element of%SIG
:{ local $SIG{TERM} = ...; # this is ok. something_that_might_raise_SIGTERM(); } # end of local scope, $SIG{TERM} restored.
- $SIG{signal} = *foo on Perl 5.8
-
"%SIG" in perlvar specifies that you can assign a signal handler with the construction
$SIG{signal} = *foo; # same as ... = \&__PACKAGE__::foo
It turns out that in Perl 5.8, this causes a fatal error when you use this type of assignment to a tied hash. This is a limitation of tied hashes in Perl 5.8, not a problem with the magic of
%SIG
. - Overhead of processing signals
-
Signals::XSIG
adds some overhead to signal processing and that could ultimately make your signal processing less stable as each signal takes longer to process. This module may not be suitable for applications where many signals need to be processed in a short time. - Using Perl debugger is more painful
-
This module hangs its hat on a lot of the same hooks that the Perl debugger needs to use. As you step through code in the debugger, you may often find yourself stepping through the code in this module (say, where some core module is installing a
$SIG{__WARN__}
handler. You may find this annoying.
Please report any bugs or feature requests to bug-signal-handler-super at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Signals-XSIG. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Signals::XSIG
You can also look for information at:
RT: CPAN's request tracker
AnnoCPAN: Annotated CPAN documentation
CPAN Ratings
Search CPAN
LICENSE AND COPYRIGHT
Copyright 2010-2013 Marty O'Brien.
This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.