File Coverage

blib/lib/IO/SigGuard.pm
Criterion Covered Total %
statement 33 34 97.0
branch 11 18 61.1
condition 2 5 40.0
subroutine 7 7 100.0
pod 1 3 33.3
total 54 67 80.6


line stmt bran cond sub pod time code
1             package IO::SigGuard;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             IO::SigGuard - Signal protection for sysread/syswrite
8              
9             =head1 SYNOPSIS
10              
11             IO::SigGuard::sysread( $fh, $buf, $size );
12             IO::SigGuard::sysread( $fh, $buf, $size, $offset );
13              
14             IO::SigGuard::syswrite( $fh, $buf );
15             IO::SigGuard::syswrite( $fh, $buf, $len );
16             IO::SigGuard::syswrite( $fh, $buf, $len, $offset );
17              
18             IO::SigGuard::select( $read, $write, $exc, $timeout );
19              
20             =head1 DESCRIPTION
21              
22             C describes how Perl versions from 5.8.0 onward disable
23             the OS’s SA_RESTART flag when installing Perl signal handlers.
24              
25             This module restores that pattern: it does an automatic restart
26             when a signal interrupts an operation, so you can entirely avoid
27             the generally-useless EINTR error when using
28             C, C, and C.
29              
30             =head1 ABOUT C and C
31              
32             Other than that you’ll never see EINTR and that
33             there are no function prototypes used (i.e., you need parentheses on
34             all invocations), C and C
35             work exactly the same as Perl’s equivalent built-ins.
36              
37             =head1 ABOUT C
38              
39             To handle EINTR, C has to subtract the elapsed time
40             from the given timeout then repeat the internal C. Because
41             the C built-in’s C<$timeleft> return is not reliable across
42             all platforms, we have to compute the elapsed time ourselves. By default the
43             only means of doing this is the C built-in, which can only measure
44             individual seconds.
45              
46             This works, but there are two ways to make it more accurate:
47              
48             =over
49              
50             =item * Have L loaded, and C will use that
51             module rather than the C built-in.
52              
53             =item * Set C<$IO::SigGuard::TIME_CR> to a compatible code reference. This is
54             useful, e.g., if you have your own logic to do the equivalent of
55             L—for example, in Linux you may prefer to call the C
56             system call directly from Perl to avoid L’s XS overhead.
57              
58             =back
59              
60             In scalar contact, C is a drop-in replacement
61             for Perl’s 4-argument built-in.
62              
63             In list context, there may be discrepancies re the C<$timeleft> value
64             that Perl returns from a call to C
65             this value is generally not reliable anyway, though, so that shouldn’t be a
66             big deal. In fact, on systems (e.g., MacOS) where the built-in’s C<$timeleft>
67             is completely useless, IO::SigGuard’s return is actually B since it
68             does provide at least a rough estimate of how much of the given timeout value
69             is left.
70              
71             See C for portability notes for C
72              
73             =cut
74              
75 3     3   293227 use strict;
  3         30  
  3         100  
76 3     3   20 use warnings;
  3         7  
  3         892  
77              
78             our $VERSION = '0.02-TRIAL7';
79              
80             #Set this in lieu of using Time::HiRes or built-in time().
81             our $TIME_CR;
82              
83             #As light as possible …
84              
85             my $read;
86              
87             sub sysread {
88             READ: {
89 683 0   683 0 10846 $read = ( (@_ == 3) ? CORE::sysread( $_[0], $_[1], $_[2] ) : (@_ == 4) ? CORE::sysread( $_[0], $_[1], $_[2], $_[3] ) : die "Wrong args count! (@_)" ) or do {
  691 50       7073873  
    100          
90 8 50       213 if ($!) {
91 8 50   2   120 redo READ if $!{'EINTR'};
  2         687  
  2         2740  
  2         22  
92             }
93             };
94             }
95              
96 683         22206 return $read;
97             }
98              
99             my $wrote;
100              
101             sub syswrite {
102 630304     630304 0 2621971 $wrote = 0;
103              
104             WRITE: {
105 630304   33     856388 $wrote += ( (@_ == 2) ? CORE::syswrite( $_[0], $_[1], length($_[1]) - $wrote, $wrote ) : (@_ == 3) ? CORE::syswrite( $_[0], $_[1], $_[2] - $wrote, $wrote ) : (@_ == 4) ? CORE::syswrite( $_[0], $_[1], $_[2] - $wrote, $_[3] + $wrote ) : die "Wrong args count! (@_)" ) || do {
  630304         2701031  
106             if ($!) {
107             redo WRITE if $!{'EINTR'}; #EINTR => file pointer unchanged
108             return undef;
109             }
110              
111             die "empty write without error??"; #unexpected!
112             };
113             }
114              
115 630304         1182546 return $wrote;
116             }
117              
118             my ($start, $last_loop_time, $os_error, $nfound, $timeleft, $timer_cr);
119              
120             #pre-5.16 didn’t have \&CORE::time.
121 27     27   98 sub _time { time }
122              
123             sub select {
124 6 50   6 1 19733 die( (caller 0)[3] . ' must have 4 arguments!' ) if @_ != 4;
125              
126 6         77 $os_error = $!;
127              
128 6   50     163 $timer_cr = $TIME_CR || Time::HiRes->can('time') || \&_time;
129              
130 6         25 $start = $timer_cr->();
131 6         16 $last_loop_time = $start;
132              
133             SELECT: {
134 6         17 ($nfound, $timeleft) = CORE::select( $_[0], $_[1], $_[2], $_[3] - $last_loop_time + $start );
  27         9523621  
135 27 100       1001 if ($nfound == -1) {
136              
137             #Use of %! will autoload Errno.pm,
138             #which can affect the value of $!.
139 21         15581 my $select_error = $!;
140              
141 21 50       325 if ($!{'EINTR'}) {
142 21         643 $last_loop_time = $timer_cr->();
143 21         108 redo SELECT;
144             }
145              
146 0         0 $! = $select_error;
147             }
148             else {
149              
150             #select() doesn’t set $! on success, so let’s not clobber what
151             #value was there before.
152 6         22 $! = $os_error;
153             }
154              
155 6 100       37 return wantarray ? ($nfound, $timeleft) : $nfound;
156             }
157             }
158              
159             =head1 REPOSITORY
160              
161             L
162              
163             =head1 AUTHOR
164              
165             Felipe Gasper (FELIPE)
166              
167             … with special thanks to Mario Roy (MARIOROY) for extra testing
168             and a few fixes/improvements.
169              
170             =head1 COPYRIGHT
171              
172             Copyright 2017 by L
173              
174             =head1 LICENSE
175              
176             This distribution is released under the same license as Perl.
177              
178             =cut
179              
180             1;