views:

38

answers:

1

I'm noticing some problems with the perlio layer in perl. Took me a good day to track it down and was hoping some other people knew something about this? The scariest thing about this is that since its so low-level, I'm worried it'll reduce the code's portability.

Server code:

use strict;
use Socket;

socket(my $sock, AF_INET, SOCK_STREAM, getprotobyname('tcp')) or die();
setsockopt($sock, SOL_SOCKET, SO_REUSEADDR, 1) or die();
bind($sock, pack_sockaddr_in(23457, inet_aton('0.0.0.0'))) or die();
listen($sock, 10) or die();

my $paddr = accept(my $csock, $sock);
if (not $paddr) { 
    die();
}
my ($port, $iaddr) = unpack_sockaddr_in($paddr);
printf "accepted %s:%s\n", inet_ntoa($iaddr), $port;
send($csock, "1234567890", 0);
recv($csock, my $tmp, 8192, 0);
close($csock);
close($sock);

Client code (that I change slightly to test):

use strict;
use Socket;
use PerlIO;

socket(my $sock, AF_INET, SOCK_STREAM, getprotobyname('tcp')) or die();
connect($sock, pack_sockaddr_in(23457, inet_aton('localhost'))) or die();
print "layers before = ".join(', ', PerlIO::get_layers($sock))."\n";
#binmode($sock, ':pop');  # uncomment this line to watch the code work...
print "layers after  = ".join(', ', PerlIO::get_layers($sock))."\n";

my $tmp;
print "1ret    = ".read($sock, $tmp, 1)."\n"; print "tmp $tmp\n"; stillpending($sock);
print "1ret    = ".read($sock, $tmp, 1)."\n"; print "tmp $tmp\n"; stillpending($sock);
print "1ret    = ".read($sock, $tmp, 1)."\n"; print "tmp $tmp\n"; stillpending($sock);
print "1ret    = ".read($sock, $tmp, 1)."\n"; print "tmp $tmp\n"; stillpending($sock);
print "8192ret = ".read($sock, $tmp, 8192)."\n"; print "tmp $tmp\n"; stillpending($sock);
send($sock, 'blah', 0);

close($sock);

Server output:

accepted 127.0.0.1:39944

Client output with binmode commented (perlio layer in use):

layers before = unix, perlio
layers after  = unix, perlio
1ret    = 1
tmp 1
no more
1ret    = 1
tmp 2
no more
1ret    = 1
tmp 3
no more
1ret    = 1
tmp 4
no more

Above blocks forever.

Client output with binmode uncommented (no perlio layer in use):

layers before = unix, perlio
layers after  = unix
1ret    = 1
tmp 1
still more
1ret    = 1
tmp 2
still more
1ret    = 1
tmp 3
still more
1ret    = 1
tmp 4
still more
8192ret = 6
tmp 567890
no more

My problem is that select() stop returning that data is pending when obviously (via strace) the first read() call has consumed the entire output sent by the server (into some internal buffer I imagine). Upon which the last read(..., 8192) will also block when without the perlio layer, it does not block.

I guess I have the solution to my problem (pop the perlio layer), but I'm curious what other peoples thoughts are? Is it a bug that select() reports no more data pending even though the first perl read (with perlio layer) has read everything into memory?

Has anyone else run into similar issues?

+8  A: 

This is expected: if you're using select(), you need to use sysread() instead of read(), because of the buffering (as you've discovered :).

From perldoc -f select:

WARNING: One should not attempt to mix buffered I/O (like "read" or ) with "select", except as permitted by POSIX, and even then only on POSIX systems. You have to use "sysread" instead.

psmears
Ah, yikes... I've probably read that a hundred times, but forgot about that :-/ Thanks for pointing that out.
xyld