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             =cut
72              
73 3     3   197652 use strict;
  3         7  
  3         74  
74 3     3   13 use warnings;
  3         3  
  3         782  
75              
76             our $VERSION = '0.02-TRIAL3';
77              
78             #Set this in lieu of using Time::HiRes or built-in time().
79             our $TIME_CR;
80              
81             #As light as possible …
82              
83             my $read;
84              
85             sub sysread {
86             READ: {
87 742 0   742 0 7449 $read = ( (@_ == 3) ? CORE::sysread( $_[0], $_[1], $_[2] ) : (@_ == 4) ? CORE::sysread( $_[0], $_[1], $_[2], $_[3] ) : die "Wrong args count! (@_)" ) or do {
  784 50       7499937  
    100          
88 42 50       914 if ($!) {
89 42 50   2   587 redo READ if $!{'EINTR'};
  2         486  
  2         1992  
  2         14  
90             }
91             };
92             }
93              
94 742         7541 return $read;
95             }
96              
97             my $wrote;
98              
99             sub syswrite {
100 755707     755707 0 2320292 $wrote = 0;
101              
102             WRITE: {
103 755707   33     830778 $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 {
  755707         3317797  
104             if ($!) {
105             redo WRITE if $!{'EINTR'}; #EINTR => file pointer unchanged
106             return undef;
107             }
108              
109             die "empty write without error??"; #unexpected!
110             };
111             }
112              
113 755707         1126244 return $wrote;
114             }
115              
116             my ($start, $last_loop_time, $os_error, $nfound, $timeleft, $timer_cr);
117              
118             #pre-5.16 didn’t have \&CORE::time.
119 27     27   88 sub _time { time }
120              
121             sub select {
122 6 50   6 1 17551 die( (caller 0)[3] . ' must have 4 arguments!' ) if @_ != 4;
123              
124 6         65 $os_error = $!;
125              
126 6   50     135 $timer_cr = $TIME_CR || Time::HiRes->can('time') || \&_time;
127              
128 6         18 $start = $timer_cr->();
129 6         15 $last_loop_time = $start;
130              
131             SELECT: {
132 6         15 ($nfound, $timeleft) = CORE::select( $_[0], $_[1], $_[2], $_[3] - $last_loop_time + $start );
  27         9516087  
133 27 100       652 if ($nfound == -1) {
134 21 50       12230 if ($!{'EINTR'}) {
135 21         498 $last_loop_time = $timer_cr->();
136 21         113 redo SELECT;
137             }
138             }
139             else {
140              
141             #select() doesn’t set $! on success, so let’s not clobber what
142             #value was there before.
143 6         14 $! = $os_error;
144             }
145              
146 6 100       47 return wantarray ? ($nfound, $timeleft) : $nfound;
147             }
148             }
149              
150             =head1 REPOSITORY
151              
152             L
153              
154             =head1 AUTHOR
155              
156             Felipe Gasper (FELIPE)
157              
158             … with special thanks to Mario Roy (MARIOROY) for extra testing
159             and a few fixes/improvements.
160              
161             =head1 COPYRIGHT
162              
163             Copyright 2017 by L
164              
165             =head1 LICENSE
166              
167             This distribution is released under the same license as Perl.
168              
169             =cut
170              
171             1;