use IO::Select;
{
# Actually, I should have one buffer per filehandle, but
# for simplicity, I'll assume you only ever call
# this function with one filehandle.
my $buffer = "";
# Note that timeout is for waiting for the \n after the
# first character arrives. This function will block
# forever waiting for the first character of the line
sub readWithTimeout
{
my ($inputh, $timeout) = @_;
if (length($buffer)==0) {
sysread $inputh,$buffer,500;
if (length($buffer)==0) {return undef;}
}
if ($buffer =~ s{^(.*\n)}{}) {return $1;}
my $s = IO::Select->new();
$s->add($inputh);
my @ready = $s->can_read($timeout);
while (@ready)
{
last unless
sysread $inputh,$buffer,500,length($buffer);
if ($buffer =~ s{^(.*\n)}{}) {return $1;}
@ready = $s->can_read($timeout);
}
$buffer =~ s/.+// or return undef;
return $&;
}
}
####
package TimeOutHandle;
use Tie::Handle;
use IO::Select;
@ISA = qw(Tie::Handle);
sub TIEHANDLE
{
my ($class, $wrappedfh, $timeout) = @_;
my ($buffer) = '';
$timeout ||= 5; # default five second timeout
return bless [$wrappedfh, $timeout, \$buffer];
}
sub READLINE
{
my $self = shift;
my ($inputh, $timeout, $bufref) = @$self;
if (length($$bufref)==0) {
sysread $inputh,$$bufref,500;
if (length($$bufref)==0) {return undef;}
}
if ($$bufref =~ s{^.*\n}{}) {return $&;}
my $s = IO::Select->new();
$s->add($inputh);
my @ready = $s->can_read($timeout);
while (@ready)
{
last unless
sysread $inputh,$$bufref,500,length($$bufref);
if ($$bufref =~ s{^.*\n}{}) {return $&;}
@ready = $s->can_read($timeout);
}
$$bufref =~ s/.+// or return undef;
return $&;
}
package main;
# Test code for the above.
# feed me some data slowly
sub slowdata()
{
print "a"; sleep 3; print "b"; sleep 6;
print "c\nd"; sleep 2; print "e";
}
# given a filehandle, tell me what <> does.
# Also, give me when <> does it.
sub reportlines
{
my $fh = shift;
printf "%02d: __BEGIN__\n", time() % 100;
while (<$fh>)
{ s/\n/\\n/s; printf "%02d: '$_'\n", time() % 100; }
printf "%02d: __END__\n", time() % 100;
}
$|=1;
print "Regular filehandle:\n";
open (INDATA, '-|') || do {slowdata();exit();};
reportlines(*INDATA);
print "\n5 second timeout:\n";
open (INDATA, '-|') || do {slowdata();exit();};
tie *TIMEOUT5, 'TimeOutHandle', \*INDATA;
reportlines(*TIMEOUT5);
print "\n1 second timeout:\n";
open (INDATA, '-|') || do {slowdata();exit();};
tie *TIMEOUT1, 'TimeOutHandle', \*INDATA, 1;
reportlines(*TIMEOUT1);
print "\n10 second timeout:\n";
open (INDATA, '-|') || do {slowdata();exit();};
tie *TIMEOUT10, 'TimeOutHandle', \*INDATA, 10;
reportlines(*TIMEOUT10);
####
Regular filehandle:
19: __BEGIN__
28: 'abc\n'
30: 'de'
30: __END__
5 second timeout:
30: __BEGIN__
38: 'ab'
39: 'c\n'
41: 'de'
41: __END__
1 second timeout:
41: __BEGIN__
42: 'a'
45: 'b'
50: 'c\n'
51: 'd'
52: 'e'
52: __END__
10 second timeout:
52: __BEGIN__
61: 'abc\n'
63: 'de'
63: __END__