File Coverage

blib/lib/IO/SigGuard.pm
Criterion Covered Total %
statement 32 32 100.0
branch 11 18 61.1
condition 2 5 40.0
subroutine 7 7 100.0
pod 1 3 33.3
total 53 65 81.5


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   177260 use strict;
  3         6  
  3         69  
76 3     3   14 use warnings;
  3         5  
  3         559  
77              
78             our $VERSION = '0.02-TRIAL4';
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 721 0   721 0 9321 $read = ( (@_ == 3) ? CORE::sysread( $_[0], $_[1], $_[2] ) : (@_ == 4) ? CORE::sysread( $_[0], $_[1], $_[2], $_[3] ) : die "Wrong args count! (@_)" ) or do {
  780 50       7322538  
    100          
90 59 50       1381 if ($!) {
91 59 50   2   912 redo READ if $!{'EINTR'};
  2         470  
  2         1951  
  2         14  
92             }
93             };
94             }
95              
96 721         10261 return $read;
97             }
98              
99             my $wrote;
100              
101             sub syswrite {
102 758040     758040 0 2339608 $wrote = 0;
103              
104             WRITE: {
105 758040   33     797305 $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 {
  758040         3355414  
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 758040         1124273 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   79 sub _time { time }
122              
123             sub select {
124 6 50   6 1 14138 die( (caller 0)[3] . ' must have 4 arguments!' ) if @_ != 4;
125              
126 6         53 $os_error = $!;
127              
128 6   50     106 $timer_cr = $TIME_CR || Time::HiRes->can('time') || \&_time;
129              
130 6         17 $start = $timer_cr->();
131 6         13 $last_loop_time = $start;
132              
133             SELECT: {
134 6         11 ($nfound, $timeleft) = CORE::select( $_[0], $_[1], $_[2], $_[3] - $last_loop_time + $start );
  27         9518598  
135 27 100       547 if ($nfound == -1) {
136 21 50       10548 if ($!{'EINTR'}) {
137 21         418 $last_loop_time = $timer_cr->();
138 21         85 redo SELECT;
139             }
140             }
141             else {
142              
143             #select() doesn’t set $! on success, so let’s not clobber what
144             #value was there before.
145 6         12 $! = $os_error;
146             }
147              
148 6 100       23 return wantarray ? ($nfound, $timeleft) : $nfound;
149             }
150             }
151              
152             =head1 REPOSITORY
153              
154             L
155              
156             =head1 AUTHOR
157              
158             Felipe Gasper (FELIPE)
159              
160             … with special thanks to Mario Roy (MARIOROY) for extra testing
161             and a few fixes/improvements.
162              
163             =head1 COPYRIGHT
164              
165             Copyright 2017 by L
166              
167             =head1 LICENSE
168              
169             This distribution is released under the same license as Perl.
170              
171             =cut
172              
173             1;