Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eof() on non-blocking filehandle #7275

Open
p5pRT opened this issue Apr 30, 2004 · 4 comments
Open

eof() on non-blocking filehandle #7275

p5pRT opened this issue Apr 30, 2004 · 4 comments

Comments

@p5pRT
Copy link

p5pRT commented Apr 30, 2004

Migrated from rt.perl.org#29277 (status was 'open')

Searchable as RT29277$

@p5pRT
Copy link
Author

p5pRT commented Apr 30, 2004

From perl-5.8.0@ton.iguana.be

Created by perl-5.8.0@ton.iguana.be

Using this testprogram​:

#! /usr/bin/perl -wl
use strict;
use IO​::Socket​::INET;
use POSIX qw(F_GETFL F_SETFL O_NONBLOCK);

pipe(my $rd, my $wr) || die "Could not create pipe​: $!";
my $flags = fcntl($rd, F_GETFL, 0) || die "Can't get fcntl flags​: $!\n";
fcntl($rd, F_SETFL, $flags | O_NONBLOCK) || die "Can't set fcntl flags​: $!\n";
select($wr);
$|=1;
close($wr) if @​ARGV;
my $rc = eof($rd);
print STDERR defined($rc) ? $rc : "undef", "/$!";

Running it with an argument​:
1/Illegal seek

In an strace near the end I see​:

pipe([3, 4]) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE, 0xbfffeb30) = -1 EINVAL (Invalid argument)
_llseek(3, 0, 0xbfffeb80, SEEK_CUR) = -1 ESPIPE (Illegal seek)
ioctl(4, SNDCTL_TMR_TIMEBASE, 0xbfffeb30) = -1 EINVAL (Invalid argument)
_llseek(4, 0, 0xbfffeb80, SEEK_CUR) = -1 ESPIPE (Illegal seek)
fcntl64(3, F_SETFD, FD_CLOEXEC) = 0
fcntl64(4, F_SETFD, FD_CLOEXEC) = 0
fcntl64(3, F_GETFL) = 0 (flags O_RDONLY)
fcntl64(3, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
close(4) = 0
read(3, "", 4096) = 0
brk(0) = 0x82bc800
brk(0x82be000) = 0x82be000
write(2, "1", 11) = 1
write(2, "/Illegal seek", 13/Illegal seek) = 13
write(2, "\n", 1
) = 1
close(3) = 0

So the code indeed saw the EOF, but it reports it with errno set
to a bit strange value, but ok.

Now without argument (not closing the write side of the pipe)​:
1/Illegal seek

mm, but there is no EOF this time....

Again looking at the strace​:

pipe([3, 4]) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE, 0xbfffeb40) = -1 EINVAL (Invalid argument)
_llseek(3, 0, 0xbfffeb90, SEEK_CUR) = -1 ESPIPE (Illegal seek)
ioctl(4, SNDCTL_TMR_TIMEBASE, 0xbfffeb40) = -1 EINVAL (Invalid argument)
_llseek(4, 0, 0xbfffeb90, SEEK_CUR) = -1 ESPIPE (Illegal seek)
fcntl64(3, F_SETFD, FD_CLOEXEC) = 0
fcntl64(4, F_SETFD, FD_CLOEXEC) = 0
fcntl64(3, F_GETFL) = 0 (flags O_RDONLY)
fcntl64(3, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
read(3, 0x8175804, 4096) = -1 EAGAIN (Resource temporarily unavailable)
brk(0) = 0x82bc800
brk(0x82be000) = 0x82be000
write(2, "1", 11) = 1
write(2, "/Illegal seek", 13/Illegal seek) = 13
write(2, "\n", 1
) = 1
close(3) = 0
close(4) = 0

So the read got EAGAIN (so perl should know it's not really EOF),
but eof() still returns true, but i think it should have returned
something false here. The errno is also strange (seemingly that old
ESPIPE got saved and later restored, overwriting the EAGAIN).

Perl Info

Flags:
    category=core
    severity=low

Site configuration information for perl v5.8.2:

Configured by ton at Sun Jan  4 19:19:06 CET 2004.

Summary of my perl5 (revision 5.0 version 8 subversion 2) configuration:
  Platform:
    osname=linux, osvers=2.6.0, archname=i686-linux-64int-ld
    uname='linux quasar 2.6.0 #3 thu dec 18 18:22:48 cet 2003 i686 gnulinux '
    config_args=''
    hint=recommended, useposix=true, d_sigaction=define
    usethreads=undef use5005threads=undef useithreads=undef usemultiplicity=undef
    useperlio=define d_sfio=undef uselargefiles=define usesocks=undef
    use64bitint=define use64bitall=undef uselongdouble=define
    usemymalloc=y, bincompat5005=undef
  Compiler:
    cc='cc', ccflags ='-fno-strict-aliasing -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
    optimize='-O2 -fomit-frame-pointer',
    cppflags='-fno-strict-aliasing -I/usr/local/include'
    ccversion='', gccversion='3.4.0 20031231 (experimental)', gccosandvers=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=12345678
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
    ivtype='long long', ivsize=8, nvtype='long double', nvsize=12, Off_t='off_t', lseeksize=8
    alignbytes=4, prototype=define
  Linker and Libraries:
    ld='cc', ldflags =' -L/usr/local/lib'
    libpth=/usr/local/lib /lib /usr/lib
    libs=-lnsl -ldb -ldl -lm -lcrypt -lutil -lc
    perllibs=-lnsl -ldl -lm -lcrypt -lutil -lc
    libc=/lib/libc-2.3.2.so, so=so, useshrplib=false, libperl=libperl.a
    gnulibc_version='2.3.2'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-rdynamic'
    cccdlflags='-fpic', lddlflags='-shared -L/usr/local/lib'

Locally applied patches:
    


@INC for perl v5.8.2:
    /usr/lib/perl5/5.8.2/i686-linux-64int-ld
    /usr/lib/perl5/5.8.2
    /usr/lib/perl5/site_perl/5.8.2/i686-linux-64int-ld
    /usr/lib/perl5/site_perl/5.8.2
    /usr/lib/perl5/site_perl
    .


Environment for perl v5.8.2:
    HOME=/home/ton
    LANG (unset)
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/home/ton/bin.Linux:/home/ton/bin:/home/ton/bin.SampleSetup:/usr/local/bin:/usr/local/sbin:/usr/local/jre/bin:/home/oracle/product/9.0.1/bin:/usr/local/ar/bin:/usr/games/bin:/usr/X11R6/bin:/usr/share/bin:/usr/bin:/usr/sbin:/bin:/sbin:.
    PERL_BADLANG (unset)
    SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented May 4, 2004

From @iabyn

On Fri, Apr 30, 2004 at 12​:09​:05PM -0000, perl-5. 8. 0 @​ ton. iguana. be wrote​:

Using this testprogram​:

#! /usr/bin/perl -wl
use strict;
use IO​::Socket​::INET;
use POSIX qw(F_GETFL F_SETFL O_NONBLOCK);

pipe(my $rd, my $wr) || die "Could not create pipe​: $!";
my $flags = fcntl($rd, F_GETFL, 0) || die "Can't get fcntl flags​: $!\n";
fcntl($rd, F_SETFL, $flags | O_NONBLOCK) || die "Can't set fcntl flags​: $!\n";
select($wr);
$|=1;
close($wr) if @​ARGV;
my $rc = eof($rd);
print STDERR defined($rc) ? $rc : "undef", "/$!";

Running it with an argument​:
1/Illegal seek

The value of $! is irrelevant; it's only relevant on failed perl system
functions.

Now without argument (not closing the write side of the pipe)​:
1/Illegal seek

mm, but there is no EOF this time....

Again looking at the strace​:

pipe([3, 4]) = 0
ioctl(3, SNDCTL_TMR_TIMEBASE, 0xbfffeb40) = -1 EINVAL (Invalid argument)
_llseek(3, 0, 0xbfffeb90, SEEK_CUR) = -1 ESPIPE (Illegal seek)
ioctl(4, SNDCTL_TMR_TIMEBASE, 0xbfffeb40) = -1 EINVAL (Invalid argument)
_llseek(4, 0, 0xbfffeb90, SEEK_CUR) = -1 ESPIPE (Illegal seek)
fcntl64(3, F_SETFD, FD_CLOEXEC) = 0
fcntl64(4, F_SETFD, FD_CLOEXEC) = 0
fcntl64(3, F_GETFL) = 0 (flags O_RDONLY)
fcntl64(3, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
read(3, 0x8175804, 4096) = -1 EAGAIN (Resource temporarily unavailable)
brk(0) = 0x82bc800
brk(0x82be000) = 0x82be000
write(2, "1", 11) = 1
write(2, "/Illegal seek", 13/Illegal seek) = 13
write(2, "\n", 1
) = 1
close(3) = 0
close(4) = 0

So the read got EAGAIN (so perl should know it's not really EOF),
but eof() still returns true, but i think it should have returned
something false here. The errno is also strange (seemingly that old
ESPIPE got saved and later restored, overwriting the EAGAIN).

Perl's eof is implemneted by going a getc and ungetc equivalent;
since these can change $!, but the Perl function eof() is documented
as not seeing $!, the old value has to be saved then restored.
Agin, the value of $! here is a bit of red herring.

The real issues, are whether eof() should be clever enough to understand
and handle non-blocking I/O (I presume that EAGAIN is system specific, at
least outside the UNIX world!), or whether this falls into the category
of mixing raw and buffered I/O, and thus get everything you deserve?

I guess NI-S might be able to comment better on this.

Dave.

--
"The GPL violates the U.S. Constitution, together with copyright,
antitrust and export control laws"
  -- SCO smoking crack again.

@p5pRT
Copy link
Author

p5pRT commented May 4, 2004

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented May 5, 2004

From perl5-porters@ton.iguana.be

In article <20040504215002.GE1884@​iabyn.com>,
  Dave Mitchell <davem@​iabyn.com> writes​:

Perl's eof is implemneted by going a getc and ungetc equivalent;
since these can change $!, but the Perl function eof() is documented
as not seeing $!, the old value has to be saved then restored.
Agin, the value of $! here is a bit of red herring.

Well, not completely. Indeed $! only gets a guaranteed value after
a failing systemcall, and stdio functions aren't even systemcalls.
However, the way it normally works is that perl doesn't play with $!
(or errno at the C level really), and just lets libc do its job.
In particular if I use perl operators corresponding to stdio functions,
I usually expect $! to show what the corresponding stdio function
would return.

But yeah, that's just an expectation, not guaranteed in the perl docs,
so I wasn't calling the $! behaviour a bug. But still, I find the
save/restore behaviour that eof() does on $! unexpected and it also
seems pointless (my version of the eof() docs (perl 5.8.2) at least
don't say that $! gets saved and restored).

The real issues, are whether eof() should be clever enough to understand
and handle non-blocking I/O (I presume that EAGAIN is system specific, at
least outside the UNIX world!), or whether this falls into the category
of mixing raw and buffered I/O, and thus get everything you deserve?

Using stdio functions on non-blocking sockets is not what I personally
would do, but it's a valid programming style nevertheless.
However it works at the systemcall level not visible for the perl user,
I think eof() should return some false value for the case of a non-closed
non-blocking socket (undef would be my personal preference), and while
$! probably won't be documented as anything particular, naively I would
expect to see EAGAIN. And by not trying to be clever about $! save/restore,
I think that would actually happen automatically.

Trying to formulate what I'd want to happen purely in terms of
getc/ungetc, I also notice that if in my test program I replace
eof($rd) with getc($rd), I see​:

undef/Bad file descriptor

in both cases (closed or non-closed non-blocking socket). The getc() docs
DO talk about $!, but only say that in the error case it will have a value.
It implies (but indeed doesn't say) that for the eof case, $! should be
empty, otherwise you cannot distinguish the two cases (error versus eof).

And "Bad file descriptor" is even a very unexpected error for the
non-closed case (where supposedly the docs say I should get a valid $!).
It's not the correct errno nor does an strace show any EBADF happening on
any of the system calls.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants