File Coverage

blib/lib/IO/BufferedSelect.pm
Criterion Covered Total %
statement 9 42 21.4
branch 0 18 0.0
condition 0 6 0.0
subroutine 3 5 60.0
pod 2 2 100.0
total 14 73 19.1


line stmt bran cond sub pod time code
1             package IO::BufferedSelect;
2              
3 1     1   23416 use strict;
  1         2  
  1         35  
4 1     1   5 use warnings;
  1         2  
  1         25  
5 1     1   890 use IO::Select;
  1         1985  
  1         618  
6              
7             =head1 NAME
8              
9             IO::BufferedSelect - Line-buffered select interface
10              
11             =head1 SYNOPSIS
12              
13             use IO::BufferedSelect;
14             my $bs = new BufferedSelect($fh1, $fh2);
15             while(1)
16             {
17             my @ready = $bs->read_line();
18             foreach(@ready)
19             {
20             my ($fh, $line) = @$_;
21             my $fh_name = ($fh == $fh1 ? "fh1" : "fh2");
22             print "$fh_name: $line";
23             }
24             }
25              
26             =head1 DESCRIPTION
27              
28             The C
29             multiple streams simultaneously, blocking until one or more of them is ready for
30             reading or writing. Unfortunately, this requires us to use C and
31             C rather than Perl's buffered I/O functions. In the case of reading,
32             there are two issues with combining C
33             might block but the data we want is already in Perl's input buffer, ready to
34             be slurped in by C; and (2) C
35             available, but C will block because there isn't a full
36             C<$/>-terminated line available.
37              
38             The purpose of this module is to implement a buffered version of the C
39             interface that operates on I, rather than characters. Given a set of
40             filehandles, it will block until a full line is available on one or more of
41             them.
42              
43             Note that this module is currently limited, in that (1) it only does C
44             for readability, not writability or exceptions; and (2) it does not support
45             arbitrary line separators (C<$/>): lines must be delimited by newlines.
46              
47             =cut
48              
49             our $VERSION = '1.0';
50              
51             =head1 CONSTRUCTOR
52              
53             =over
54              
55             =item new ( HANDLES )
56              
57             Create a C object for a set of filehandles. Note that because
58             this class buffers input from these filehandles internally, you should B
59             use the C object for reading from them (you shouldn't read from
60             them directly or pass them to other BufferedSelect instances).
61              
62             =back
63              
64             =cut
65              
66             sub new($@)
67             {
68 0     0 1   my $class = shift;
69 0           my @handles = @_;
70              
71 0           my $self = { handles => \@handles,
72 0           buffers => [ map { '' } @handles ],
73 0           eof => [ map { 0 } @handles ],
74             selector => new IO::Select( @handles ) };
75              
76 0           return bless $self;
77             }
78              
79             =head1 METHODS
80              
81             =over
82              
83             =item read_line
84              
85             =item read_line ($timeout)
86              
87             =item read_line ($timeout, @handles)
88              
89             Block until a line is available on one of the filehandles. If C<$timeout> is
90             C, it blocks indefinitely; otherwise, it returns after at most
91             C<$timeout> seconds.
92              
93             If C<@handles> is specified, then only these filehandles will be considered;
94             otherwise, it will use all filehandles passed to the constructor.
95              
96             Returns a list of pairs S>, where C<$fh> is a filehandle and
97             C<$line> is the line that was read (including the newline, ala C). If
98             the filehandle reached EOF, then C<$line> will be undef. Note that "reached
99             EOF" is to be interpreted in the buffered sense: if a filehandle is at EOF but
100             there are newline-terminated lines in C's buffer, C
101             will continue to return lines until the buffer is empty.
102              
103             =cut
104              
105             sub read_line($;$@)
106             {
107 0     0 1   my $self = shift;
108 0           my ($timeout, @handles) = @_;
109              
110             # Convert @handles to a "set" of indices
111 0           my %use_idx = ();
112 0 0         if(@handles)
113             {
114 0           foreach my $idx( 0..$#{$self->{handles}} )
  0            
115             {
116 0 0         $use_idx{$idx} = 1 if grep { $_ == $self->{handles}->[$idx] } @handles;
  0            
117             }
118             }
119             else
120             {
121 0           $use_idx{$_} = 1 foreach( 0..$#{$self->{handles}} );
  0            
122             }
123              
124 0           for( my $is_first = 1 ; 1 ; $is_first = 0 )
125             {
126             # If we have any lines in buffers, return those first
127 0           my @result = ();
128              
129 0           foreach my $idx( 0..$#{$self->{handles}} )
  0            
130             {
131 0 0         next unless $use_idx{$idx};
132              
133 0 0         if($self->{buffers}->[$idx] =~ s/(.*\n)//)
    0          
134             {
135 0           push @result, [ $self->{handles}->[$idx], $1 ];
136             }
137             elsif($self->{eof}->[$idx])
138             {
139             # NOTE: we discard any unterminated data at EOF
140 0           push @result, [ $self->{handles}->[$idx], undef ];
141             }
142             }
143              
144             # Only give it one shot if $timeout is defined
145 0 0 0       return @result if ( @result or (defined($timeout) and !$is_first) );
      0        
146              
147             # Do a select(), optionally with a timeout
148 0           my @ready = $self->{selector}->can_read( $timeout );
149              
150             # Read into $self->{buffers}
151 0           foreach my $fh( @ready )
152             {
153 0           foreach my $idx( 0..$#{$self->{handles}} )
  0            
154             {
155 0 0         next unless $fh == $self->{handles}->[$idx];
156 0 0         next unless $use_idx{$idx};
157 0           my $bytes = sysread $fh, $self->{buffers}->[$idx], 1024, length $self->{buffers}->[$idx];
158 0 0         $self->{eof}->[$idx] = 1 if($bytes == 0);
159             }
160             }
161             }
162             }
163              
164              
165             1;
166              
167             __END__