File Coverage

blib/lib/IO/SigGuard.pm
Criterion Covered Total %
statement 31 31 100.0
branch 11 18 61.1
condition 2 5 40.0
subroutine 6 6 100.0
pod 1 3 33.3
total 51 63 80.9


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   273528 use strict;
  3         9  
  3         102  
74 3     3   22 use warnings;
  3         8  
  3         1072  
75              
76             our $VERSION = '0.02-TRIAL2';
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 708 0   708 0 12212 $read = ( (@_ == 3) ? CORE::sysread( $_[0], $_[1], $_[2] ) : (@_ == 4) ? CORE::sysread( $_[0], $_[1], $_[2], $_[3] ) : die "Wrong args count! (@_)" ) or do {
  716 50       7233182  
    100          
88 8 50       243 if ($!) {
89 8 50   2   132 redo READ if $!{'EINTR'};
  2         564  
  2         2419  
  2         18  
90             }
91             };
92             }
93              
94 708         21634 return $read;
95             }
96              
97             my $wrote;
98              
99             sub syswrite {
100 571729     571729 0 2666583 $wrote = 0;
101              
102             WRITE: {
103 571729   33     843998 $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 {
  571729         2639566  
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 571729         1227186 return $wrote;
114             }
115              
116             my ($start, $last_loop_time, $os_error, $nfound, $timeleft, $timer_cr);
117              
118             sub select {
119 6 50   6 1 21686 die( (caller 0)[3] . ' must have 4 arguments!' ) if @_ != 4;
120              
121 6         81 $os_error = $!;
122              
123 6   50     158 $timer_cr = $TIME_CR || Time::HiRes->can('time') || \&CORE::time;
124              
125 6         31 $start = $timer_cr->();
126 6         20 $last_loop_time = $start;
127              
128             SELECT: {
129 6         11 ($nfound, $timeleft) = CORE::select( $_[0], $_[1], $_[2], $_[3] - $last_loop_time + $start );
  27         9516403  
130 27 100       590 if ($nfound == -1) {
131 21 50       15904 if ($!{'EINTR'}) {
132 21         605 $last_loop_time = $timer_cr->();
133 21         278 redo SELECT;
134             }
135             }
136             else {
137              
138             #select() doesn’t set $! on success, so let’s not clobber what
139             #value was there before.
140 6         23 $! = $os_error;
141             }
142              
143 6 100       49 return wantarray ? ($nfound, $timeleft) : $nfound;
144             }
145             }
146              
147             =head1 REPOSITORY
148              
149             L
150              
151             =head1 AUTHOR
152              
153             Felipe Gasper (FELIPE)
154              
155             … with special thanks to Mario Roy (MARIOROY) for extra testing
156             and a few fixes/improvements.
157              
158             =head1 COPYRIGHT
159              
160             Copyright 2017 by L
161              
162             =head1 LICENSE
163              
164             This distribution is released under the same license as Perl.
165              
166             =cut
167              
168             1;