Acme-Sort-Sleep
view release on metacpan or search on metacpan
local/lib/perl5/IO/Async/Loop.pm view on Meta::CPAN
# You may distribute under the terms of either the GNU General Public License
# or the Artistic License (the same terms as Perl itself)
#
# (C) Paul Evans, 2007-2015 -- leonerd@leonerd.org.uk
package IO::Async::Loop;
use strict;
use warnings;
our $VERSION = '0.70';
# When editing this value don't forget to update the docs below
use constant NEED_API_VERSION => '0.33';
# Base value but some classes might override
use constant _CAN_ON_HANGUP => 0;
# Most Loop implementations do not accurately handle sub-second timers.
# This only matters for unit tests
use constant _CAN_SUBSECOND_ACCURATELY => 0;
# Does the loop implementation support IO_ASYNC_WATCHDOG?
use constant _CAN_WATCHDOG => 0;
# Watchdog configuration constants
use constant WATCHDOG_ENABLE => $ENV{IO_ASYNC_WATCHDOG};
use constant WATCHDOG_INTERVAL => $ENV{IO_ASYNC_WATCHDOG_INTERVAL} || 10;
use constant WATCHDOG_SIGABRT => $ENV{IO_ASYNC_WATCHDOG_SIGABRT};
use Carp;
use IO::Socket (); # empty import
use Time::HiRes qw(); # empty import
use POSIX qw( WNOHANG );
use Scalar::Util qw( refaddr weaken );
use Socket qw( SO_REUSEADDR AF_INET6 IPPROTO_IPV6 IPV6_V6ONLY );
use IO::Async::OS;
use constant HAVE_SIGNALS => IO::Async::OS->HAVE_SIGNALS;
use constant HAVE_POSIX_FORK => IO::Async::OS->HAVE_POSIX_FORK;
use constant HAVE_THREADS => IO::Async::OS->HAVE_THREADS;
# Never sleep for more than 1 second if a signal proxy is registered, to avoid
# a borderline race condition.
# There is a race condition in perl involving signals interacting with XS code
# that implements blocking syscalls. There is a slight chance a signal will
# arrive in the XS function, before the blocking itself. Perl will not run our
# (safe) deferred signal handler in this case. To mitigate this, if we have a
# signal proxy, we'll adjust the maximal timeout. The signal handler will be
# run when the XS function returns.
our $MAX_SIGWAIT_TIME = 1;
# Also, never sleep for more than 1 second if the OS does not support signals
# and we have child watches registered (so we must use waitpid() polling)
our $MAX_CHILDWAIT_TIME = 1;
# Maybe our calling program will have a suggested hint of a specific Loop
# class or list of classes to use
our $LOOP;
# Undocumented; used only by the test scripts.
# Setting this value true will avoid the IO::Async::Loop::$^O candidate in the
# magic constructor
our $LOOP_NO_OS;
# SIGALRM handler for watchdog
$SIG{ALRM} = sub {
# There are two extra frames here; this one and the signal handler itself
local $Carp::CarpLevel = $Carp::CarpLevel + 2;
if( WATCHDOG_SIGABRT ) {
print STDERR Carp::longmess( "Watchdog timeout" );
kill ABRT => $$;
}
else {
Carp::confess( "Watchdog timeout" );
}
} if WATCHDOG_ENABLE;
$SIG{PIPE} = "IGNORE" if ( $SIG{PIPE} || "" ) eq "DEFAULT";
=head1 NAME
C<IO::Async::Loop> - core loop of the C<IO::Async> framework
=head1 SYNOPSIS
use IO::Async::Stream;
use IO::Async::Timer::Countdown;
use IO::Async::Loop;
my $loop = IO::Async::Loop->new;
$loop->add( IO::Async::Timer::Countdown->new(
delay => 10,
on_expire => sub { print "10 seconds have passed\n" },
)->start );
$loop->add( IO::Async::Stream->new_for_stdin(
on_read => sub {
my ( $self, $buffref, $eof ) = @_;
while( $$buffref =~ s/^(.*)\n// ) {
print "You typed a line $1\n";
}
return 0;
},
) );
$loop->run;
=head1 DESCRIPTION
This module provides an abstract class which implements the core loop of the
L<IO::Async> framework. Its primary purpose is to store a set of
L<IO::Async::Notifier> objects or subclasses of them. It handles all of the
lower-level set manipulation actions, and leaves the actual IO readiness
testing/notification to the concrete class that implements it. It also
provides other functionality such as signal handling, child process managing,
and timers.
See also the two bundled Loop subclasses:
=over 4
=item L<IO::Async::Loop::Select>
=item L<IO::Async::Loop::Poll>
=back
Or other subclasses that may appear on CPAN which are not part of the core
L<IO::Async> distribution.
=head2 Ignoring SIGPIPE
Since version I<0.66> loading this module automatically ignores C<SIGPIPE>, as
it is highly unlikely that the default-terminate action is the best course of
action for an L<IO::Async>-based program to take. If at load time the handler
disposition is still set as C<DEFAULT>, it is set to ignore. If already
another handler has been placed there by the program code, it will be left
undisturbed.
=cut
# Internal constructor used by subclasses
sub __new
{
my $class = shift;
# Detect if the API version provided by the subclass is sufficient
$class->can( "API_VERSION" ) or
die "$class is too old for IO::Async $VERSION; it does not provide \->API_VERSION\n";
$class->API_VERSION >= NEED_API_VERSION or
die "$class is too old for IO::Async $VERSION; we need API version >= ".NEED_API_VERSION.", it provides ".$class->API_VERSION."\n";
WATCHDOG_ENABLE and !$class->_CAN_WATCHDOG and
warn "$class cannot implement IO_ASYNC_WATCHDOG\n";
my $self = bless {
notifiers => {}, # {nkey} = notifier
iowatches => {}, # {fd} = [ $on_read_ready, $on_write_ready, $on_hangup ]
sigattaches => {}, # {sig} => \@callbacks
childmanager => undef,
childwatches => {}, # {pid} => $code
threadwatches => {}, # {tid} => $code
timequeue => undef,
deferrals => [],
os => {}, # A generic scratchpad for IO::Async::OS to store whatever it wants
}, $class;
# It's possible this is a specific subclass constructor. We still want the
# magic IO::Async::Loop->new constructor to yield this if it's the first
# one
our $ONE_TRUE_LOOP ||= $self;
# Legacy support - temporary until all CPAN classes are updated; bump NEEDAPI version at that point
my $old_timer = $self->can( "enqueue_timer" ) != \&enqueue_timer;
if( $old_timer != ( $self->can( "cancel_timer" ) != \&cancel_timer ) ) {
die "$class should overload both ->enqueue_timer and ->cancel_timer, or neither";
}
if( $old_timer ) {
warnings::warnif( deprecated => "Enabling old_timer workaround for old loop class " . $class );
}
$self->{old_timer} = $old_timer;
return $self;
}
=head1 MAGIC CONSTRUCTOR
=head2 new
$loop = IO::Async::Loop->new
This function attempts to find a good subclass to use, then calls its
constructor. It works by making a list of likely candidate classes, then
trying each one in turn, C<require>ing the module then calling its C<new>
method. If either of these operations fails, the next subclass is tried. If
no class was successful, then an exception is thrown.
The constructed object is cached, and will be returned again by a subsequent
call. The cache will also be set by a constructor on a specific subclass. This
behaviour makes it possible to simply use the normal constructor in a module
that wishes to interract with the main program's Loop, such as an integration
module for another event system.
For example, the following two C<$loop> variables will refer to the same
object:
use IO::Async::Loop;
use IO::Async::Loop::Poll;
my $loop_poll = IO::Async::Loop::Poll->new;
my $loop = IO::Async::Loop->new;
While it is not advised to do so under normal circumstances, if the program
really wishes to construct more than one Loop object, it can call the
constructor C<really_new>, or invoke one of the subclass-specific constructors
directly.
The list of candidates is formed from the following choices, in this order:
=over 4
=item * $ENV{IO_ASYNC_LOOP}
If this environment variable is set, it should contain a comma-separated list
of subclass names. These names may or may not be fully-qualified; if a name
does not contain C<::> then it will have C<IO::Async::Loop::> prepended to it.
This allows the end-user to specify a particular choice to fit the needs of
his use of a program using L<IO::Async>.
=item * $IO::Async::Loop::LOOP
If this scalar is set, it should contain a comma-separated list of subclass
names. These may or may not be fully-qualified, as with the above case. This
allows a program author to suggest a loop module to use.
In cases where the module subclass is a hard requirement, such as GTK programs
using C<Glib>, it would be better to use the module specifically and invoke
its constructor directly.
=item * IO::Async::OS->LOOP_PREFER_CLASSES
local/lib/perl5/IO/Async/Loop.pm view on Meta::CPAN
Returns a new L<IO::Async::Future> instance with a reference to the Loop.
=cut
sub new_future
{
my $self = shift;
require IO::Async::Future;
return IO::Async::Future->new( $self );
}
=head2 await
$loop->await( $future )
Blocks until the given future is ready, as indicated by its C<is_ready> method.
As a convenience it returns the future, to simplify code:
my @result = $loop->await( $future )->get;
=cut
sub await
{
my $self = shift;
my ( $future ) = @_;
$self->loop_once until $future->is_ready;
return $future;
}
=head2 await_all
$loop->await_all( @futures )
Blocks until all the given futures are ready, as indicated by the C<is_ready>
method. Equivalent to calling C<await> on a C<< Future->wait_all >> except
that it doesn't create the surrounding future object.
=cut
sub _all_ready { $_->is_ready or return 0 for @_; return 1 }
sub await_all
{
my $self = shift;
my @futures = @_;
$self->loop_once until _all_ready @futures;
}
=head2 delay_future
$loop->delay_future( %args )->get
Returns a new L<IO::Async::Future> instance which will become done at a given
point in time. The C<%args> should contain an C<at> or C<after> key as per the
C<watch_time> method. The returned future may be cancelled to cancel the
timer. At the alloted time the future will succeed with an empty result list.
=cut
sub delay_future
{
my $self = shift;
my %args = @_;
my $future = $self->new_future;
my $id = $self->watch_time( %args,
code => sub { $future->done },
);
$future->on_cancel( sub { shift->loop->unwatch_time( $id ) } );
return $future;
}
=head2 timeout_future
$loop->timeout_future( %args )->get
Returns a new L<IO::Async::Future> instance which will fail at a given point
in time. The C<%args> should contain an C<at> or C<after> key as per the
C<watch_time> method. The returned future may be cancelled to cancel the
timer. At the alloted time, the future will fail with the string C<"Timeout">.
=cut
sub timeout_future
{
my $self = shift;
my %args = @_;
my $future = $self->new_future;
my $id = $self->watch_time( %args,
code => sub { $future->fail( "Timeout" ) },
);
$future->on_cancel( sub { shift->loop->unwatch_time( $id ) } );
return $future;
}
############
# Features #
############
=head1 FEATURES
Most of the following methods are higher-level wrappers around base
functionality provided by the low-level API documented below. They may be
used by L<IO::Async::Notifier> subclasses or called directly by the program.
The following methods documented with a trailing call to C<< ->get >> return
L<Future> instances.
=cut
sub __new_feature
{
my $self = shift;
my ( $classname ) = @_;
( my $filename = "$classname.pm" ) =~ s{::}{/}g;
require $filename;
# These features aren't supposed to be "user visible", so if methods called
# on it carp or croak, the shortmess line ought to skip IO::Async::Loop and
# go on report its caller. To make this work, add the feature class to our
# @CARP_NOT list.
push our(@CARP_NOT), $classname;
return $classname->new( loop => $self );
}
=head2 attach_signal
$id = $loop->attach_signal( $signal, $code )
This method adds a new signal handler to watch the given signal. The same
signal can be attached to multiple times; its callback functions will all be
invoked, in no particular order.
The returned C<$id> value can be used to identify the signal handler in case
it needs to be removed by the C<detach_signal> method. Note that this value
local/lib/perl5/IO/Async/Loop.pm view on Meta::CPAN
A CODE reference to the handling callback.
=back
There can only be one callback per signal name. Registering a new one will
remove an existing one.
Applications should use a L<IO::Async::Signal> object, or call
C<attach_signal> instead of using this method.
This and C<unwatch_signal> are optional; a subclass may implement neither, or
both. If it implements neither then signal handling will be performed by the
base class using a self-connected pipe to interrupt the main IO blocking.
=cut
sub watch_signal
{
my $self = shift;
my ( $signal, $code ) = @_;
HAVE_SIGNALS or croak "This OS cannot ->watch_signal";
IO::Async::OS->loop_watch_signal( $self, $signal, $code );
}
=head2 unwatch_signal
$loop->unwatch_signal( $signal )
This method removes the signal callback for the given signal.
=over 8
=item $signal
The name of the signal to watch to. This should be a bare name like C<TERM>.
=back
=cut
sub unwatch_signal
{
my $self = shift;
my ( $signal ) = @_;
HAVE_SIGNALS or croak "This OS cannot ->unwatch_signal";
IO::Async::OS->loop_unwatch_signal( $self, $signal );
}
=head2 watch_time
$id = $loop->watch_time( %args )
This method installs a callback which will be called at the specified time.
The time may either be specified as an absolute value (the C<at> key), or
as a delay from the time it is installed (the C<after> key).
The returned C<$id> value can be used to identify the timer in case it needs
to be cancelled by the C<unwatch_time> method. Note that this value may be
an object reference, so if it is stored, it should be released after it has
been fired or cancelled, so the object itself can be freed.
The C<%params> hash takes the following keys:
=over 8
=item at => NUM
The absolute system timestamp to run the event.
=item after => NUM
The delay after now at which to run the event, if C<at> is not supplied. A
zero or negative delayed timer should be executed as soon as possible; the
next time the C<loop_once> method is invoked.
=item now => NUM
The time to consider as now if calculating an absolute time based on C<after>;
defaults to C<time()> if not specified.
=item code => CODE
CODE reference to the continuation to run at the allotted time.
=back
Either one of C<at> or C<after> is required.
For more powerful timer functionality as a L<IO::Async::Notifier> (so it can
be used as a child within another Notifier), see instead the
L<IO::Async::Timer> object and its subclasses.
These C<*_time> methods are optional; a subclass may implement neither or both
of them. If it implements neither, then the base class will manage a queue of
timer events. This queue should be handled by the C<loop_once> method
implemented by the subclass, using the C<_adjust_timeout> and
C<_manage_queues> methods.
This is the newer version of the API, replacing C<enqueue_timer>. It is
unspecified how this method pair interacts with the older
C<enqueue/requeue/cancel_timer> triplet.
=cut
sub watch_time
{
my $self = shift;
my %args = @_;
# Renamed args
if( exists $args{after} ) {
$args{delay} = delete $args{after};
}
elsif( exists $args{at} ) {
$args{time} = delete $args{at};
}
else {
croak "Expected one of 'at' or 'after'";
}
if( $self->{old_timer} ) {
$self->enqueue_timer( %args );
}
else {
my $timequeue = $self->{timequeue} ||= $self->__new_feature( "IO::Async::Internals::TimeQueue" );
my $time = $self->_build_time( %args );
my $code = $args{code};
$timequeue->enqueue( time => $time, code => $code );
}
}
=head2 unwatch_time
$loop->unwatch_time( $id )
Removes a timer callback previously created by C<watch_time>.
This is the newer version of the API, replacing C<cancel_timer>. It is
unspecified how this method pair interacts with the older
C<enqueue/requeue/cancel_timer> triplet.
=cut
sub unwatch_time
{
my $self = shift;
my ( $id ) = @_;
if( $self->{old_timer} ) {
$self->cancel_timer( $id );
}
else {
my $timequeue = $self->{timequeue} ||= $self->__new_feature( "IO::Async::Internals::TimeQueue" );
$timequeue->cancel( $id );
}
}
sub _build_time
{
my $self = shift;
my %params = @_;
my $time;
if( exists $params{time} ) {
$time = $params{time};
}
elsif( exists $params{delay} ) {
my $now = exists $params{now} ? $params{now} : $self->time;
$time = $now + $params{delay};
}
else {
croak "Expected either 'time' or 'delay' keys";
}
return $time;
}
=head2 enqueue_timer
$id = $loop->enqueue_timer( %params )
An older version of C<watch_time>. This method should not be used in new code
but is retained for legacy purposes. For simple watch/unwatch behaviour use
instead the new C<watch_time> method; though note it has differently-named
arguments. For requeueable timers, consider using an
L<IO::Async::Timer::Countdown> or L<IO::Async::Timer::Absolute> instead.
=cut
sub enqueue_timer
{
my $self = shift;
my ( %params ) = @_;
# Renamed args
$params{after} = delete $params{delay} if exists $params{delay};
$params{at} = delete $params{time} if exists $params{time};
my $code = $params{code};
return [ $self->watch_time( %params ), $code ];
}
=head2 cancel_timer
$loop->cancel_timer( $id )
An older version of C<unwatch_time>. This method should not be used in new
code but is retained for legacy purposes.
=cut
sub cancel_timer
{
my $self = shift;
my ( $id ) = @_;
$self->unwatch_time( $id->[0] );
}
=head2 requeue_timer
$newid = $loop->requeue_timer( $id, %params )
Reschedule an existing timer, moving it to a new time. The old timer is
removed and will not be invoked.
The C<%params> hash takes the same keys as C<enqueue_timer>, except for the
C<code> argument.
The requeue operation may be implemented as a cancel + enqueue, which may
mean the ID changes. Be sure to store the returned C<$newid> value if it is
required.
This method should not be used in new code but is retained for legacy
purposes. For requeueable, consider using an L<IO::Async::Timer::Countdown> or
L<IO::Async::Timer::Absolute> instead.
=cut
sub requeue_timer
{
my $self = shift;
my ( $id, %params ) = @_;
$self->unwatch_time( $id->[0] );
return $self->enqueue_timer( %params, code => $id->[1] );
}
=head2 watch_idle
$id = $loop->watch_idle( %params )
This method installs a callback which will be called at some point in the near
future.
The C<%params> hash takes the following keys:
=over 8
=item when => STRING
Specifies the time at which the callback will be invoked. See below.
=item code => CODE
CODE reference to the continuation to run at the allotted time.
=back
The C<when> parameter defines the time at which the callback will later be
invoked. Must be one of the following values:
=over 8
=item later
Callback is invoked after the current round of IO events have been processed
by the loop's underlying C<loop_once> method.
If a new idle watch is installed from within a C<later> callback, the
installed one will not be invoked during this round. It will be deferred for
the next time C<loop_once> is called, after any IO events have been handled.
=back
If there are pending idle handlers, then the C<loop_once> method will use a
zero timeout; it will return immediately, having processed any IO events and
idle handlers.
The returned C<$id> value can be used to identify the idle handler in case it
needs to be removed, by calling the C<unwatch_idle> method. Note this value
may be a reference, so if it is stored it should be released after the
callback has been invoked or cancled, so the referrant itself can be freed.
This and C<unwatch_idle> are optional; a subclass may implement neither, or
both. If it implements neither then idle handling will be performed by the
base class, using the C<_adjust_timeout> and C<_manage_queues> methods.
=cut
sub watch_idle
{
my $self = shift;
my %params = @_;
my $code = delete $params{code};
local/lib/perl5/IO/Async/Loop.pm view on Meta::CPAN
{
my $self = shift;
my ( $pid, $code ) = @_;
my $childwatches = $self->{childwatches};
croak "Already have a handler for $pid" if exists $childwatches->{$pid};
if( HAVE_SIGNALS and !$self->{childwatch_sigid} ) {
$self->{childwatch_sigid} = $self->attach_signal(
CHLD => sub { _reap_children( $childwatches ) }
);
# There's a chance the child has already exited
my $zid = waitpid( $pid, WNOHANG );
if( defined $zid and $zid > 0 ) {
my $exitstatus = $?;
$self->later( sub { $code->( $pid, $exitstatus ) } );
return;
}
}
$childwatches->{$pid} = $code;
}
=head2 unwatch_child
$loop->unwatch_child( $pid )
This method removes a watch on an existing child process PID.
=cut
sub unwatch_child
{
my $self = shift;
my ( $pid ) = @_;
my $childwatches = $self->{childwatches};
delete $childwatches->{$pid};
if( HAVE_SIGNALS and !keys %$childwatches ) {
$self->detach_signal( CHLD => delete $self->{childwatch_sigid} );
}
}
=head1 METHODS FOR SUBCLASSES
The following methods are provided to access internal features which are
required by specific subclasses to implement the loop functionality. The use
cases of each will be documented in the above section.
=cut
=head2 _adjust_timeout
$loop->_adjust_timeout( \$timeout )
Shortens the timeout value passed in the scalar reference if it is longer in
seconds than the time until the next queued event on the timer queue. If there
are pending idle handlers, the timeout is reduced to zero.
=cut
sub _adjust_timeout
{
my $self = shift;
my ( $timeref, %params ) = @_;
$$timeref = 0, return if @{ $self->{deferrals} };
if( defined $self->{sigproxy} and !$params{no_sigwait} ) {
$$timeref = $MAX_SIGWAIT_TIME if !defined $$timeref or $$timeref > $MAX_SIGWAIT_TIME;
}
if( !HAVE_SIGNALS and keys %{ $self->{childwatches} } ) {
$$timeref = $MAX_CHILDWAIT_TIME if !defined $$timeref or $$timeref > $MAX_CHILDWAIT_TIME;
}
my $timequeue = $self->{timequeue};
return unless defined $timequeue;
my $nexttime = $timequeue->next_time;
return unless defined $nexttime;
my $now = exists $params{now} ? $params{now} : $self->time;
my $timer_delay = $nexttime - $now;
if( $timer_delay < 0 ) {
$$timeref = 0;
}
elsif( !defined $$timeref or $timer_delay < $$timeref ) {
$$timeref = $timer_delay;
}
}
=head2 _manage_queues
$loop->_manage_queues
Checks the timer queue for callbacks that should have been invoked by now, and
runs them all, removing them from the queue. It also invokes all of the
pending idle handlers. Any new idle handlers installed by these are not
invoked yet; they will wait for the next time this method is called.
=cut
sub _manage_queues
{
my $self = shift;
my $count = 0;
my $timequeue = $self->{timequeue};
$count += $timequeue->fire if $timequeue;
my $deferrals = $self->{deferrals};
$self->{deferrals} = [];
foreach my $code ( @$deferrals ) {
$code->();
$count++;
}
my $childwatches = $self->{childwatches};
if( !HAVE_SIGNALS and keys %$childwatches ) {
_reap_children( $childwatches );
}
return $count;
}
=head1 EXTENSIONS
An Extension is a Perl module that provides extra methods in the
C<IO::Async::Loop> or other packages. They are intended to provide extra
functionality that easily integrates with the rest of the code.
Certain base methods take an C<extensions> parameter; an ARRAY reference
containing a list of extension names. If such a list is passed to a method, it
will immediately call a method whose name is that of the base method, prefixed
by the first extension name in the list, separated by C<_>. If the
C<extensions> list contains more extension names, it will be passed the
remaining ones in another C<extensions> parameter.
For example,
$loop->connect(
extensions => [qw( FOO BAR )],
%args
)
will become
$loop->FOO_connect(
extensions => [qw( BAR )],
%args
)
This is provided so that extension modules, such as L<IO::Async::SSL> can
easily be invoked indirectly, by passing extra arguments to C<connect> methods
or similar, without needing every module to be aware of the C<SSL> extension.
This functionality is generic and not limited to C<SSL>; other extensions may
also use it.
The following methods take an C<extensions> parameter:
$loop->connect
$loop->listen
If an extension C<listen> method is invoked, it will be passed a C<listener>
parameter even if one was not provided to the original C<< $loop->listen >>
call, and it will not receive any of the C<on_*> event callbacks. It should
use the C<acceptor> parameter on the C<listener> object.
=cut
=head1 STALL WATCHDOG
A well-behaved L<IO::Async> program should spend almost all of its time
blocked on input using the underlying C<IO::Async::Loop> instance. The stall
watchdog is an optional debugging feature to help detect CPU spinlocks and
other bugs, where control is not returned to the loop every so often.
If the watchdog is enabled and an event handler consumes more than a given
amount of real time before returning to the event loop, it will be interrupted
by printing a stack trace and terminating the program. The watchdog is only in
effect while the loop itself is not blocking; it won't fail simply because the
loop instance is waiting for input or timers.
It is implemented using C<SIGALRM>, so if enabled, this signal will no longer
be available to user code. (Though in any case, most uses of C<alarm()> and
C<SIGALRM> are better served by one of the L<IO::Async::Timer> subclasses).
The following environment variables control its behaviour.
=over 4
=item IO_ASYNC_WATCHDOG => BOOL
Enables the stall watchdog if set to a non-zero value.
=item IO_ASYNC_WATCHDOG_INTERVAL => INT
Watchdog interval, in seconds, to pass to the C<alarm(2)> call. Defaults to 10
seconds.
=item IO_ASYNC_WATCHDOG_SIGABRT => BOOL
If enabled, the watchdog signal handler will raise a C<SIGABRT>, which usually
has the effect of breaking out of a running program in debuggers such as
F<gdb>. If not set then the process is terminated by throwing an exception with
C<die>.
=back
=cut
=head1 AUTHOR
Paul Evans <leonerd@leonerd.org.uk>
=cut
0x55AA;
( run in 1.845 second using v1.01-cache-2.11-cpan-39bf76dae61 )