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