NAME
Signals::XSIG - install multiple signal handlers through %XSIG
VERSION
Version 1.00
SYNOPSIS
use Signals::XSIG;
# 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;
# On SIGTERM, prehandle_sigterm, handle_sigterm, and posthandle_sigterm
# are called, 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";
# turn off all %XSIG signal handling, restore %SIG to its state
# before Signals::XSIG was imported
unimport Signals::XSIG;
DESCRIPTION
Perl provides the magic global hash variable %SIG
to make it easy to trap and set a custom signal handler (see "%SIG" in perlvar and perlipc) on most of the available signals. 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. This module solves this issue by allowing you to install a list of handlers for a signal, and installing 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 users of your module can still install their own
SIGUSR1
handler through$SIG{USR1}
without interfering with your ownSIGUSR1
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
-
Behaves identically 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.
When is this feature useful? Perhaps when Signals::XSIG
makes the wrong assumptions about what a default signal behavior is. Or when you have an unusual system with different default signal behavior than your typical system, and out of portability concerns you want your unusual system to behave the way you are used to.
EXPORT
The %XSIG
extended signal handler hash is exported into the calling namespace by default.
FUNCTIONS
unimport
To disable this module and restore %SIG
to its original state (before this module was imported), call
unimport Signals::XSIG;
import
To reactivate this module and enable %XSIG
signal handling after calling "unimport", call
import Signals::XSIG
Selectively disabling this module with local { no Signals::XSIG; ... }
blocks is not yet supported.
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 (typically terminating the program):
$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 still only be invoked once when that signal is trapped.
AUTHOR
Marty O'Brien, <mob at cpan.org>
BUGS AND LIMITATIONS
Using this module may make it more difficult to use Perl in some other ways.
local %SIG
not DWIM
Version of Signals::XSIG
prior to 1.00 did not work well when a caller would local
ize %SIG
, and those versions strongly recommended using Signals::XSIG
in any program than also used local %SIG
. It is safe to call local %SIG
with Signals::XSIG
now, though it probably will not do what you expect. In older perls, local %SIG
with Signals::XSIG
has no effect at all. In newer perls, setting a signal handler on a local %SIG
with Signals::XSIG
will not be used to handle a signal.
Note that it is and has always been 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 the implementation of 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 many 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.
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
Search CPAN
Please report any bugs or feature requests to bug-signals-xsig 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.
ACKNOWLEDGEMENTS
This module was greatly simplified thanks to a suggestion from Leon Timmermans
LICENSE AND COPYRIGHT
Copyright 2010-2022 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.