=encoding utf-8

=head1 NAME

IO::SigGuard - SA_RESTART in pure Perl

=head1 SYNOPSIS

    IO::SigGuard::sysread( $fh, $buf, $size );
    IO::SigGuard::sysread( $fh, $buf, $size, $offset );

    IO::SigGuard::syswrite( $fh, $buf );
    IO::SigGuard::syswrite( $fh, $buf, $len );
    IO::SigGuard::syswrite( $fh, $buf, $len, $offset );

    IO::SigGuard::select( $read, $write, $exc, $timeout );

=head1 DESCRIPTION

C<perldoc perlipc> describes how Perl versions from 5.8.0 onward disable
the OS’s SA_RESTART flag when installing Perl signal handlers.

This module imitates that pattern in pure Perl: it does an automatic
restart when a signal interrupts an operation so you can avoid
the generally-useless EINTR error when using
C<sysread()>, C<syswrite()>, and C<select()>.

For this to work, whatever signal handler you implement will need to break
out of this module, probably via either C<die()> or C<exit()>.

=head1 ABOUT C<sysread()> and C<syswrite()>

Other than that you’ll never see EINTR and that
there are no function prototypes used (i.e., you need parentheses on
all invocations), C<sysread()> and C<syswrite()>
work exactly the same as Perl’s equivalent built-ins.

=head1 ABOUT C<select()>

To handle EINTR, C<IO::SigGuard::select()> has to subtract the elapsed time
from the given timeout then repeat the internal C<select()>. Because
the C<select()> built-in’s C<$timeleft> return is not reliable across
all platforms, we have to compute the elapsed time ourselves. By default the
only means of doing this is the C<time()> built-in, which can only measure
individual seconds.

This works, but there are two ways to make it more accurate:

=over

=item * Have L<Time::HiRes> loaded, and C<IO::SigGuard::select()> will use that
module rather than the C<time()> built-in.

=item * Set C<$IO::SigGuard::TIME_CR> to a compatible code reference. This is
useful, e.g., if you have your own logic to do the equivalent of
L<Time::HiRes>—for example, in Linux you may prefer to call the C<gettimeofday>
system call directly from Perl to avoid L<Time::HiRes>’s XS overhead.

=back

In scalar contact, C<IO::SigGuard::select()> is a drop-in replacement
for Perl’s 4-argument built-in.

In list context, there may be discrepancies re the C<$timeleft> value
that Perl returns from a call to C<select>. As per Perl’s documentation
this value is generally not reliable anyway, though, so that shouldn’t be a
big deal. In fact, on systems like MacOS where the built-in’s C<$timeleft>
is completely useless, IO::SigGuard’s return is actually B<better> since it
does provide at least a rough estimate of how much of the given timeout value
is left.

See C<perlport> for portability notes for C<select>.

=head1 TODO

This pattern could probably be extended to C<send>, C<recv>, C<flock>, and
other system calls that can receive EINTR. If there’s a desire for that I’ll
consider adding it.

=head1 REPOSITORY

L<https://github.com/FGasper/p5-IO-SigGuard>

=head1 AUTHOR

Felipe Gasper (FELIPE)

… with special thanks to Mario Roy (MARIOROY) for extra testing
and a few fixes/improvements.

=head1 COPYRIGHT

Copyright 2017 by L<Gasper Software Consulting|http://gaspersoftware.com>

=head1 LICENSE

This distribution is released under the same license as Perl.