Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Re^4: reading from a file after a seek isn't working for me

by ikegami (Patriarch)
on Oct 22, 2009 at 01:57 UTC ( [id://802590]=note: print w/replies, xml ) Need Help??


in reply to Re^3: reading from a file after a seek isn't working for me
in thread reading from a file after a seek isn't working for me

For this reason, the filedescriptor is considered invalid from the PerlIO point of view

But it's not, or at least not completely invalid. You can still seek using the handle and print to the handle without problem. For example, adding

seek(STDOUT, -0, 2) or die $!; print STDOUT "abc\n";

does indeed append "abc\n" to the file.

It's more like Perl remembers the handle's original mode and doesn't realize it can read from it now.

Update: I did a bit of Dumping and stracing of my own.

There's is no difference in the IO objects. I'm now with you leaning towards a PerlIO problem.

Seems that the "Bad file descriptor" message originates from Perl, not the system. Perl doesn't even attempt to read from STDOUT.

$ cat a.pl use Devel::Peek; open(SAVOUT, '>&STDOUT') or die $!; close(STDOUT) if $ARGV[0]; open(STDOUT, '+>', "/tmp/stdout.log") or die $!; Dump(*STDOUT{IO}); @argv = qw(/bin/echo hello world); system(@argv); print SAVOUT "before=", tell(STDOUT), "\n"; seek(STDOUT, 0, 0) or die $!; print SAVOUT "after=", tell(STDOUT), "\n"; while (1) { my $rv = read STDOUT, $_, 8192; die $! if !defined($rv); last unless $_; print SAVOUT "stdout=", $_; } print SAVOUT "at end=", tell(STDOUT), "\n"; close STDOUT; $ diff -u <(strace perl a.pl 0 2>&1) <(strace perl a.pl 1 2>&1) | less ... lseek(1, 0, SEEK_SET) = 0 lseek(1, 0, SEEK_CUR) = 0 -[ code to read locale-dependent version of error message] -write(2, "Bad file descriptor at a.pl line"..., 37Bad file descriptor + at a.pl line 17. -) = 37 +read(1, "hello world\n", 4096) = 12 +read(1, "", 4096) = 0 +close(1) = 0 -write(3, "before=0\nafter=0\n", 17before=0 +write(3, "before=0\nafter=0\nstdout=hello wo"..., 46before=0 after=0 -) = 17 +stdout=hello world +at end=12 +) = 46 close(3) = 0 -exit_group(9) = ? -Process 4028 detached +exit_group(0) = ? +Process 4032 detached

Replies are listed 'Best First'.
Re^5: reading from a file after a seek isn't working for me
by almut (Canon) on Oct 22, 2009 at 07:23 UTC
    But it's not, or at least not completely invalid. ...

    Good point.  Actually, when taking a closer look, I think Perl sets EBADF one routine further down in PerlIOBase_read() (which is being called from the macro Perl_PerlIO_or_Base),  in case the PERLIO_F_CANREAD flag isn't set:

    PerlIOBase_read(pTHX_ PerlIO *f, void *vbuf, Size_t count) { STDCHAR *buf = (STDCHAR *) vbuf; if (f) { if (!(PerlIOBase(f)->flags & PERLIO_F_CANREAD)) { PerlIOBase(f)->flags |= PERLIO_F_ERROR; SETERRNO(EBADF, SS_IVCHAN); return 0; } ...

    It's more like Perl remembers the handle's original mode and doesn't realize it can read from it now.

    Yes, and that's most likely because the dup2 doesn't copy the perl-internal PERLIO* flags (well, how should it, it knows nothing about them).

    The following snippet shows that the two STDOUTs modes differ depending on whether STDOUT is explicitly being closed first:

    (I made use of Inline::C because I couldn't find a way to call PerlIO_modestr() directly via plain Perl)

    #!/usr/bin/perl use Inline C; close STDOUT if $ARGV[0]; open(STDOUT, '+>', "/tmp/stdout.log") or die $!; dumpmode(STDOUT); __END__ __C__ void dumpmode(SV* fh) { char buf[10]; PerlIO *f = IoIFP(sv_2io(fh)); PerlIO_modestr(f, buf); fprintf(stderr, "mode = %s\n", buf); }

    Output:

    $ ./802590.pl 0 mode = w $ ./802590.pl 1 # with explicit close mode = r+

    Not really sure why it says "r+" instead of "w+", but I suspect it's because the "+>" internally maps to the same mode as "+<", after having clobbered the file...

    Also, if you set PERLIO_DEBUG, you can see that the "w+" mode is being applied to the PerlIO layers of fd 1 only in case it is properly closed/opened:

    $ PERLIO_DEBUG=/dev/tty ./802517.pl 1 # with explicit close ... ./802517.pl:0 openn(perlio,'(Null)','Iw',1,0,0,(nil),0,(nil)) ./802517.pl:0 Layer 0 is unix ./802517.pl:0 Layer 0 is unix ./802517.pl:0 PerlIO_push f=0x6253c0 unix w 0x603b08 ./802517.pl:0 fd 1 refcnt=1 ./802517.pl:0 PerlIO_push f=0x6253c0 perlio Iw 0x603b08 ./802517.pl:0 Layer 1 is perlio ... ./802517.pl:15 openn(perlio,'','w+',-1,0,0,(nil),1,0x60a178) ./802517.pl:15 Layer 0 is unix ./802517.pl:15 Layer 0 is unix ./802517.pl:15 PerlIO_push f=0x6253c0 unix w+ 0x603b08 ./802517.pl:15 fd 1 refcnt=1 ./802517.pl:15 PerlIO_push f=0x6253c0 perlio w+ 0x603b08

    Otherwise, the "w+" is being applied to a different fd (here fd 8), and thus disappears together with the fd when it is closed (after the dup2):

    $ PERLIO_DEBUG=/dev/tty ./802517.pl 0 ... ./802517.pl:0 openn(perlio,'(Null)','Iw',1,0,0,(nil),0,(nil)) ./802517.pl:0 Layer 0 is unix ./802517.pl:0 Layer 0 is unix ./802517.pl:0 PerlIO_push f=0x6253c0 unix w 0x603b08 ./802517.pl:0 fd 1 refcnt=1 ./802517.pl:0 PerlIO_push f=0x6253c0 perlio Iw 0x603b08 ./802517.pl:0 Layer 1 is perlio ... ./802517.pl:15 openn(perlio,'','w+',-1,0,0,(nil),1,0x60a178) ./802517.pl:15 Layer 0 is unix ./802517.pl:15 Layer 0 is unix ./802517.pl:15 PerlIO_push f=0x6253e8 unix w+ 0x603b08 ./802517.pl:15 fd 8 refcnt=1 ./802517.pl:15 PerlIO_push f=0x6253e8 perlio w+ 0x603b08 ./802517.pl:15 fd 8 refcnt=0 ./802517.pl:15 PerlIO_pop f=0x6253e8 perlio ./802517.pl:15 PerlIO_pop f=0x6253e8 unix

    (irrelevant parts snippet)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://802590]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (2)
As of 2024-04-20 06:00 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found