Bug 30162 - Document when sequential I/O with named pipes works
Summary: Document when sequential I/O with named pipes works
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libfortran (show other bugs)
Version: 4.3.0
: P4 enhancement
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL: http://gcc.gnu.org/ml/gcc-patches/201...
Keywords: documentation
Depends on:
Blocks:
 
Reported: 2006-12-12 04:28 UTC by Jerry DeLisle
Modified: 2015-02-01 13:20 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2006-12-28 23:11:10


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jerry DeLisle 2006-12-12 04:28:40 UTC
Reduced test case frim Bud Davis

$ cat a.f
      integer status
      integer i
      open(unit=20,file='np',action='write',
     &     form='unformatted',iostat=status);
      print*,'status from open is ',status
      do i = 1,5
         write(20)i
      end do
      end
          


$ cat b.f
      integer status
      integer i
      integer val
      open(unit=21,file='np',action='read',
     &     form='unformatted',iostat=status);
      print*,'status from open is ',status
      do i = 1,5
         read(21)val
         print*,val
      end do



Here is what I get:

$ mkfifo np
$ ./b &
[1] 18730
$ ./a
 status from open is            0
At line 7 of file a.f
Fortran runtime error: Illegal seek
 status from open is            0
At line 8 of file b.f
Fortran runtime error: End of file
Comment 1 Thomas Koenig 2006-12-15 21:14:50 UTC
For writing, I think this fails because we

- write a bogus record marker with value 0
- write out the data
- write out the trailing record marker
- seek to the first record marker
- write out its value
- seek past the end

These seeks actually translate to OS calls, which of course fails
for pipes.

For reading, there is another problem:  If I

- redirect the output from a to a file
- cat it into the named pipe
- read it using b

it gives a bogus "corrupt unformatted sequential file" error,
for a reason that I don't understand at the moment.

Ouch.  This one is nasty.
Comment 2 Thomas Koenig 2006-12-16 11:44:44 UTC
(In reply to comment #1)

> These seeks actually translate to OS calls, which of course fails
> for pipes.

We could get by for writing if we

- eliminated the seeks as long as we don't actually go past our buffer
- flushed after every write statement if we have a non-seekable file
- set the subrecord size to the buffer length - 2*length of the record marker.
Comment 3 Thomas Koenig 2006-12-28 21:22:46 UTC
This is deep regression country; in the time
I have to devote to this, I couldn't work it out.

Unassigning myself (for now).
Comment 4 Thomas Koenig 2006-12-28 21:39:03 UTC
Additional data points:

Reading/writing unformatted sequential doesn't work with g77
(fails with an error message on open), and it fails with ifort 8
for records longer than 2**18 bytes (ifort's default buffer size).

Because of the complications with record markers, this might be
much easier to implement for stream access.

 
Comment 5 Jerry DeLisle 2006-12-28 23:11:10 UTC
I will work at it.
Comment 6 Thomas Koenig 2006-12-29 09:51:05 UTC
(In reply to comment #5)
> I will work at it.

Thanks, I'll be happy to assist with discussions and review.

(Those who can, fix; those who can't, review :-)

Comment 7 Jerry DeLisle 2007-01-01 04:58:56 UTC
I have formatted named pipe I/O working, at least for the equivalent test cases given here.
Comment 8 Thomas Koenig 2007-01-01 15:17:41 UTC
(In reply to comment #7)
> I have formatted named pipe I/O working, at least for the equivalent test cases
> given here.

Great!

If you want me to, I'll be willing to test your patch.

Thomas

Comment 9 Jerry DeLisle 2007-01-01 17:51:32 UTC
Preliminary patch for formatted only.

Index: io/unix.c
===================================================================
*** io/unix.c	(revision 120301)
--- io/unix.c	(working copy)
*************** fd_flush (unix_stream * s)
*** 349,355 ****
    size_t writelen;
  
    if (s->ndirty == 0)
!     return SUCCESS;;
  
    if (s->physical_offset != s->dirty_offset &&
        lseek (s->fd, s->dirty_offset, SEEK_SET) < 0)
--- 349,358 ----
    size_t writelen;
  
    if (s->ndirty == 0)
!     return SUCCESS;
!   
!   if (s->file_length == -1)
!     return SUCCESS;
  
    if (s->physical_offset != s->dirty_offset &&
        lseek (s->fd, s->dirty_offset, SEEK_SET) < 0)
*************** fd_sfree (unix_stream * s)
*** 562,567 ****
--- 565,574 ----
  static try
  fd_seek (unix_stream * s, gfc_offset offset)
  {
+ 
+   if (s->file_length == -1)
+     return SUCCESS;
+ 
    if (s->physical_offset == offset) /* Are we lucky and avoid syscall?  */
      {
        s->logical_offset = offset;
*************** static try
*** 583,589 ****
  fd_truncate (unix_stream * s)
  {
    if (lseek (s->fd, s->logical_offset, SEEK_SET) == -1)
!     return FAILURE;
  
    /* non-seekable files, like terminals and fifo's fail the lseek.
       Using ftruncate on a seekable special file (like /dev/null)
--- 590,596 ----
  fd_truncate (unix_stream * s)
  {
    if (lseek (s->fd, s->logical_offset, SEEK_SET) == -1)
!     return SUCCESS;
  
    /* non-seekable files, like terminals and fifo's fail the lseek.
       Using ftruncate on a seekable special file (like /dev/null)
*************** fd_to_stream (int fd, int prot)
*** 1009,1015 ****
    /* Get the current length of the file. */
  
    fstat (fd, &statbuf);
!   s->file_length = S_ISREG (statbuf.st_mode) ? statbuf.st_size : -1;
    s->special_file = !S_ISREG (statbuf.st_mode);
  
    fd_open (s);
--- 1016,1027 ----
    /* Get the current length of the file. */
  
    fstat (fd, &statbuf);
! 
!   if (lseek (fd, 0, SEEK_CUR) == (off_t) -1)
!     s->file_length = -1;
!   else
!     s->file_length = S_ISREG (statbuf.st_mode) ? statbuf.st_size : -1;
! 
    s->special_file = !S_ISREG (statbuf.st_mode);
  
    fd_open (s);
Comment 10 Thomas Koenig 2007-01-02 22:08:55 UTC
(In reply to comment #9)

> Preliminary patch for formatted only.

Looks pretty good, at least the tests pass.


> Index: io/unix.c
> ===================================================================
> *** io/unix.c   (revision 120301)
> --- io/unix.c   (working copy)
> *************** fd_flush (unix_stream * s)
> *** 349,355 ****
>     size_t writelen;
> 
>     if (s->ndirty == 0)
> !     return SUCCESS;;
> 
>     if (s->physical_offset != s->dirty_offset &&
>         lseek (s->fd, s->dirty_offset, SEEK_SET) < 0)
> --- 349,358 ----
>     size_t writelen;
> 
>     if (s->ndirty == 0)
> !     return SUCCESS;
> !   
> !   if (s->file_length == -1)
> !     return SUCCESS;

I don't understand this part.  Shouldn't we flush the
buffer anyway, even without a seek?


>   fd_truncate (unix_stream * s)
>   {
>     if (lseek (s->fd, s->logical_offset, SEEK_SET) == -1)
> !     return SUCCESS;

A small matter, we should test for seekable files first:

      if (s->file_length == -1)
        return SUCCESS;

      if (lseek(s->fd, s->logical_offset, SEEK_SET) == -1)
        return FAILURE;

If this fails, something is really wrong.

As an aside, this already works for ACCESS="stream" and unformatted
files, so I think we should think about committing this.
Comment 11 patchapp@dberlin.org 2007-01-05 05:00:43 UTC
Subject: Bug number PR30162

A patch for this bug has been added to the patch tracker.
The mailing list url for the patch is http://gcc.gnu.org/ml/gcc-patches/2007-01/msg00325.html
Comment 12 Jerry DeLisle 2007-01-06 00:14:49 UTC
Subject: Bug 30162

Author: jvdelisle
Date: Sat Jan  6 00:14:38 2007
New Revision: 120512

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=120512
Log:
2007-01-05  Jerry DeLisle  <jvdelisle@gcc.gnu.org>

	PR libgfortran/30162
	* io/unix.c (fd_flush): Don't seek if file is not seekable, defined as
	s->file_length == -1.
	(fd_alloc_w_at): Do not adjust file_length if file is not seekable.
	(fd_seek): If not seekable, just return success.
	(fd_truncate): If not seekable, no need to truncate.  Return failure if
	seek fails and the stream is not a pipe.
	(fd_to_stream): Make test for non-seekable file more robust.

Modified:
    trunk/libgfortran/ChangeLog
    trunk/libgfortran/io/unix.c

Comment 13 Jerry DeLisle 2007-01-06 21:07:56 UTC
A difficult case to deal with is:

program main
  print *,foo(0)
contains
  function foo (n) result(res)
    integer, intent(in) :: n
    integer, allocatable :: res(:)
    logical :: init = .false.

    if (.not. init) then
       allocate (res(1))
       init = .true.
    else
       allocate (res(2))
    end if

    res = n
  end function foo
end program main
Comment 14 Thomas Koenig 2007-01-06 22:26:58 UTC
Another difficult case is:

program main
  character(len=4) c
  c = 'ab  '
  write (10) trim(c)
end program main

Comment 15 Jerry DeLisle 2007-01-07 01:34:13 UTC
Subject: Bug 30162

Author: jvdelisle
Date: Sun Jan  7 01:34:03 2007
New Revision: 120544

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=120544
Log:
2007-01-06  Jerry DeLisle  <jvdelisle@gcc.gnu.org>

	PR libgfortran/30162
	* io/unix.c (fd_flush): Don't seek if file is not seekable, defined as
	s->file_length == -1.
	(fd_alloc_w_at): Do not adjust file_length if file is not seekable.
	(fd_seek): If not seekable, just return success.
	(fd_truncate): If not seekable, no need to truncate.  Return failure if
	seek fails and the stream is not a pipe.
	(fd_to_stream): Make test for non-seekable file more robust.

2007-01-06  Jerry DeLisle  <jvdelisle@gcc.gnu.org>

	* ChangeLog: Copied to ...
	* ChangeLog-2006: here.


Added:
    branches/gcc-4_2-branch/libgfortran/ChangeLog-2006
Modified:
    branches/gcc-4_2-branch/libgfortran/ChangeLog
    branches/gcc-4_2-branch/libgfortran/io/unix.c

Comment 16 Tobias Burnus 2007-02-01 09:27:54 UTC
Is this bug fixed or not? I see a 4.3 and a 4.2 check in.
Or is something missing, if yes, what?
Comment 17 Jerry DeLisle 2007-02-02 02:00:26 UTC
We do not have this working for unformatted I/O.  It will require frontend work as well as library to implement.  Specifically, the size of records, determined from the output list, must be determined prior to performing transfers.

On the library side, once the record size is known, seeking backwards in named pipes must be avoided.  Right now we seek backward to save the beginning record marker after the length of the record is determined by writing the record.  This backward seek is costly in performance.

I am convinced that these changes to frontend and library can be done.  I just do not have time right now to pursue it.  I will be studying the problem in the meantime, but I do not want to hold back anyone else from making the attempt, thinking I may be working it.
Comment 18 dagurasu 2007-08-18 19:06:45 UTC
I think this is the same bug.  If not let me know and I'll repost it.
This is a real bummer.  I use this to turn off noise from routines I don't have control of (and I mean ALOT of noise).  Maybe there is another way to do what I want though.  Anyway, the following produces a seek error on the write line.  It worked well in f77.  Now using gcc-4.1.2

       program bug
        close(6)
        open(6,file='/dev/null',status='old')
        close(6)
c  would normally do some noisy stuff here 
c    but not needed to make the bug.
        open(6,file='/dev/stdout',status='old')
        write (*,*)"Hello"
       end
Comment 19 dagurasu 2007-08-18 19:08:01 UTC
(In reply to comment #18)
> I think this is the same bug.  If not let me know and I'll repost it.
> This is a real bummer.  I use this to turn off noise from routines I don't have
> control of (and I mean ALOT of noise).  Maybe there is another way to do what I
> want though.  Anyway, the following produces a seek error on the write line. 
> It worked well in f77.  Now using gcc-4.1.2
> 
>        program bug
>         close(6)
>         open(6,file='/dev/null',status='old')
>         close(6)
> c  would normally do some noisy stuff here 
> c    but not needed to make the bug.
>         open(6,file='/dev/stdout',status='old')
>         write (*,*)"Hello"
>        end
> 

of course the comment should be one line higher.
Comment 20 Jerry DeLisle 2007-08-18 19:39:03 UTC
I will try to have a look in the next few days.
Comment 21 Jerry DeLisle 2007-08-19 19:45:58 UTC
This works on 4.2 and 4.3.  Not planning to backport to 4.1.  See if you can get a later version of gfortran.  If you have problems doing so, check the gfortran wiki binaries.  If that fails, let me know.
Comment 22 Hristo Iliev 2012-08-02 17:34:03 UTC
Revision 180701 removed all checks for special files in unit.c:unit_truncate(). As a consequence programs compiled with gfortran 4.6.3 and newer cannot to write (formatted) to Unix named pipes as ftruncate() fails with EINVAL:

(64-bit code)

lseek(3, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
write(3, " Hello, world!\n", 15)        = 15
lseek(3, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
ftruncate(3, 18446744073709551615)      = -1 EINVAL (Invalid argument)

(32-bit code)

_llseek(3, 0, 0xffba8fa0, SEEK_CUR)     = -1 ESPIPE (Illegal seek)
write(3, " Hello, world!\n", 15)        = 15
_llseek(3, 0, 0xffba9080, SEEK_CUR)     = -1 ESPIPE (Illegal seek)
ftruncate64(3, 18446744073709551615)    = -1 EINVAL (Invalid argument)
Comment 23 Janne Blomqvist 2012-08-03 19:45:10 UTC
(In reply to comment #22)
> Revision 180701 removed all checks for special files in unit.c:unit_truncate().

Yes. As the email message introducing the patch explained, special files are special in many ways, and trying to impose some kind of uniform behavior on them is bound to fail in more or less surprising and inconvenient ways. For the same reason, the code for handling special files were removed from the buffered IO functions, and all non-regular files now just use the raw IO functions instead. The only sane way is to seek/truncate only when the user code requires such a behavior, and if that then fails, return an error and let the user handle it.

The real bug is thus that we shouldn't try to seek or truncate the fd at all, as it's not necessary in this case.

I recall that I tried some simple programs with named pipes when I implemented the patch, but maybe some subsequent patch then broke it again.
Comment 24 Tobias Burnus 2012-08-06 13:43:05 UTC
(In reply to comment #23)
> The real bug is thus that we shouldn't try to seek or truncate the fd at all,
> as it's not necessary in this case.

I think the issue occurs for:

st_write_done (st_parameter_dt *dtp)
...
  if (dtp->u.p.current_unit != NULL
      && dtp->u.p.current_unit->flags.access == ACCESS_SEQUENTIAL)
    switch (dtp->u.p.current_unit->endfile)
...
      case NO_ENDFILE:
        /* Get rid of whatever is after this record.  */
        if (!is_internal_unit (dtp))
          unit_truncate (dtp->u.p.current_unit,
                         stell (dtp->u.p.current_unit->s),
                         &dtp->common);
        dtp->u.p.current_unit->endfile = AT_ENDFILE;

with a file as simple as

  open (99, file="foo.dat")
  write (99, '(a)') 'Hello world'
  end


$ mkfifo foo.dat
$ tail -f foo.dat &
[2] 342
$ gfortran test.f90 && ./a.out
At line 2 of file test.f90 (unit = 99, file = 'foo.dat')
Fortran runtime error: Invalid argument
Hello world
$ fg
tail -f foo.dat
^C
Comment 25 Tobias Burnus 2012-10-30 11:37:18 UTC
As Janne just mentioned on the gfortran mailing list, this PR is a regression. Thus, I have marked it as such. Based on the information below, It fails since GCC 4.7.

In reply to comment #22)
> Revision 180701 removed all checks for special files in unit.c:unit_truncate().
Comment 26 Thomas Koenig 2012-11-10 14:38:03 UTC
Is this caused by

http://gcc.gnu.org/viewcvs?view=revision&revision=180701

?

Maybe we need to remember if we have a special file after all, or just ignore the error on the truncate.
Comment 27 Janne Blomqvist 2012-11-10 20:21:41 UTC
(In reply to comment #26)
> Is this caused by
> 
> http://gcc.gnu.org/viewcvs?view=revision&revision=180701
> 
> ?
> 
> Maybe we need to remember if we have a special file after all, or just ignore
> the error on the truncate.

IMHO the correct fix is to not seek and/or truncate the file unless the Fortran semantics require it; that way libgfortran does not need to care whether the file is special or not. As explained in #c23, special files are special in different ways (also different on different OS'es), and trying to enumerate all the ways in which they are special is bound to fail. 

I think Tobias comment #c24 pinpoints the place which needs to be fixed, but unfortunately I haven't had time to look into it.
Comment 28 Thomas Koenig 2012-11-24 12:05:57 UTC
(In reply to comment #27)
> (In reply to comment #26)
> > Is this caused by
> > 
> > http://gcc.gnu.org/viewcvs?view=revision&revision=180701
> > 
> > ?
> > 
> > Maybe we need to remember if we have a special file after all, or just ignore
> > the error on the truncate.
> 
> IMHO the correct fix is to not seek and/or truncate the file unless the Fortran
> semantics require it; that way libgfortran does not need to care whether the
> file is special or not. As explained in #c23, special files are special in
> different ways (also different on different OS'es), and trying to enumerate all
> the ways in which they are special is bound to fail. 
> 
> I think Tobias comment #c24 pinpoints the place which needs to be fixed, but
> unfortunately I haven't had time to look into it.

Well, we need to make sure that the (very basic) program

      program main
      character*10 x
      open(10,file="foo.dat")
      write (10,'(A)') '1111'
      write (10,'(A)') '2222'
      close (10)
      open(10,file="foo.dat")
      write (10,'(A)') '3333'
      close (10)
      open(10,file="foo.dat")
 100  continue
        read (10,'(A)',end=200) x
        write (*,'(A)') x
      goto 100
 200  continue
      close(10,status="delete")
      end

continues to work as expected: That probably means truncating on
close and rewind.

I can see what I can do, but I have little time... (as always)
Comment 29 Janne Blomqvist 2012-11-25 15:08:24 UTC
(In reply to comment #28)
> (In reply to comment #27)
> > (In reply to comment #26)
> > > Is this caused by
> > > 
> > > http://gcc.gnu.org/viewcvs?view=revision&revision=180701
> > > 
> > > ?
> > > 
> > > Maybe we need to remember if we have a special file after all, or just ignore
> > > the error on the truncate.
> > 
> > IMHO the correct fix is to not seek and/or truncate the file unless the Fortran
> > semantics require it; that way libgfortran does not need to care whether the
> > file is special or not. As explained in #c23, special files are special in
> > different ways (also different on different OS'es), and trying to enumerate all
> > the ways in which they are special is bound to fail. 
> > 
> > I think Tobias comment #c24 pinpoints the place which needs to be fixed, but
> > unfortunately I haven't had time to look into it.
> 
> Well, we need to make sure that the (very basic) program
> 
>       program main
>       character*10 x
>       open(10,file="foo.dat")
>       write (10,'(A)') '1111'
>       write (10,'(A)') '2222'
>       close (10)
>       open(10,file="foo.dat")
>       write (10,'(A)') '3333'
>       close (10)
>       open(10,file="foo.dat")
>  100  continue
>         read (10,'(A)',end=200) x
>         write (*,'(A)') x
>       goto 100
>  200  continue
>       close(10,status="delete")
>       end
> 
> continues to work as expected: That probably means truncating on
> close and rewind.

I'm not sure about truncation being necessary here, actually. Where we do need to truncate is 

1) Following a write in access='sequential' mode, when we are not at the end of the file (that is, we have opened an existing file and are at the beginning, we have previously used rewind/backspace or such). This might also apply to formatted writes in access='stream' mode, need to check the standard.

2) Executing an ENDFILE statement.

3) Opening a file with status='replace' (actually, probably not, as a sensible implementation would be to open() with O_TRUNC instead of a separate ftruncate() call following open(), and while I'm too lazy to look it up right now I'm quite sure this is exactly what we already do)

I think the current code goes wrong with pipes because as they are reported to be of size 0, it is undefined whether we're at the beginning, middle, or end of the file, and hence we have made some arbitrary choice, and hit_eof() think's we're not at the end, and tries to truncate. Perhaps we need something more sophisticated such as some need_to_truncate_after_next_write flag which is set and tested at appropriate places..
Comment 30 Thomas Koenig 2012-12-14 23:07:34 UTC
This seems to do the trick.

Index: unix.c
===================================================================
--- unix.c      (Revision 194507)
+++ unix.c      (Arbeitskopie)
@@ -344,7 +344,15 @@
 static gfc_offset
 raw_tell (unix_stream * s)
 {
-  return lseek (s->fd, 0, SEEK_CUR);
+  gfc_offset x;
+  x = lseek (s->fd, 0, SEEK_CUR);
+
+  /* Non-seekable files should always be assumed to be at
+     current position.  */
+  if (x == -1 && errno == ESPIPE)
+    x = 0;
+
+  return x;
 }
 
 static gfc_offset
Comment 31 Thomas Koenig 2012-12-21 20:50:52 UTC
Author: tkoenig
Date: Fri Dec 21 20:50:48 2012
New Revision: 194679

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=194679
Log:
2012-12-21  Thomas Koenig  <tkoenig@gcc.gnu.org>

	PR libfortran/30162
	* io/unix.c (raw_tell):  If the lseek is done on a
	non-seekable file, return 0.


Modified:
    trunk/libgfortran/ChangeLog
    trunk/libgfortran/io/unix.c
Comment 32 Thomas Koenig 2012-12-22 10:46:42 UTC
Author: tkoenig
Date: Sat Dec 22 10:46:37 2012
New Revision: 194694

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=194694
Log:
2012-12-22  Thomas Koenig  <tkoenig@gcc.gnu.org>

	PR libfortran/30162
	Backport from trunk
	* io/unix.c (raw_tell):  If the lseek is done on a
	non-seekable file, return 0.


Modified:
    branches/gcc-4_7-branch/libgfortran/ChangeLog
    branches/gcc-4_7-branch/libgfortran/io/unix.c
Comment 33 Thomas Koenig 2012-12-22 10:49:08 UTC
Fixed on trunk and 4.7, closing.
Comment 34 Dominique d'Humieres 2012-12-22 12:02:52 UTC
I still see

 status from open is            0
 status from open is            0
At line 7 of file pr30162_1.f (unit = 20, file = 'np')
Fortran runtime error: Illegal seek

[1]    Exit 2                        a.exe
At line 8 of file pr30162_2.f (unit = 21, file = 'np')
Fortran runtime error: I/O past end of record on unformatted file

on x86_64-apple-darwin10 r194681.
Comment 35 tkoenig@netcologne.de 2012-12-22 15:38:08 UTC
> I still see
>
>   status from open is            0
>   status from open is            0
> At line 7 of file pr30162_1.f (unit = 20, file = 'np')
> Fortran runtime error: Illegal seek
>
> [1]    Exit 2                        a.exe
> At line 8 of file pr30162_2.f (unit = 21, file = 'np')
> Fortran runtime error: I/O past end of record on unformatted file
>
> on x86_64-apple-darwin10 r194681.

This is strange.

Can you strace (or whatever) the code and tell me what lseek()
returns?  Is it really EINVAL like the error message suggests?
If so, this would be strange, because

http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man2/lseek.2.html

tells me (as a return value)

      [EINVAL]           Whence is not a proper value.

      [ESPIPE]           Fildes is associated with a pipe, socket, or FIFO.

which indicates a bug in the library, but maybe I am looking
at the wrong manpage here.
Comment 36 Dominique d'Humieres 2012-12-22 19:13:23 UTC
> Can you strace (or whatever) the code and tell me what lseek()
> returns?  Is it really EINVAL like the error message suggests?

The best I can do (without understanding it) is

(1) if I use dtruss (should be equivalent to strace) on a, I get

[macbook] f90/bug% 25210/0x2baef9e:  lseek(0x3, 0x0, 0x1)		 = -1 Err#29
25210/0x2baef9e:  lseek(0x3, 0xFFFFFFFFFFFFFFF4, 0x1)		 = -1 Err#29

(2) if I use it on b, I get

[macbook] f90/bug% 25218/0x2baf031:  lseek(0x3, 0x0, 0x1)		 = -1 Err#29
25218/0x2baf031:  lseek(0x3, 0x4, 0x1)		 = -1 Err#29
Comment 37 tkoenig@netcologne.de 2012-12-22 20:16:07 UTC
Hi Dominique,

> The best I can do (without understanding it) is
>
> (1) if I use dtruss (should be equivalent to strace) on a, I get
>
> [macbook] f90/bug% 25210/0x2baef9e:  lseek(0x3, 0x0, 0x1)         = -1 Err#29
> 25210/0x2baef9e:  lseek(0x3, 0xFFFFFFFFFFFFFFF4, 0x1)         = -1 Err#29
>
> (2) if I use it on b, I get
>
> [macbook] f90/bug% 25218/0x2baf031:  lseek(0x3, 0x0, 0x1)         = -1 Err#29
> 25218/0x2baf031:  lseek(0x3, 0x4, 0x1)         = -1 Err#29

That doesn't point to anything suspicious.

Can you tell me what happens if you apply this patch?

Index: io/unix.c
===================================================================
--- io/unix.c   (Revision 194679)
+++ io/unix.c   (Arbeitskopie)
@@ -349,7 +349,7 @@

    /* Non-seekable files should always be assumed to be at
       current position.  */
-  if (x == -1 && errno == ESPIPE)
+  if (x == -1)
      x = 0;

    return x;
Comment 38 Dominique d'Humieres 2012-12-22 21:23:07 UTC
> Can you tell me what happens if you apply this patch?

The behavior stays unchanged;-(
Comment 39 Thomas Koenig 2012-12-22 22:14:48 UTC
Unfortunately, I cannot really debug this without access to a machine
where it fails.

Does this also fail on another BSD-based machine?  Is such a machine
available on the gcc compile farm?
Comment 40 Dominique d'Humieres 2012-12-23 14:16:52 UTC
> Unfortunately, I cannot really debug this without access to a machine
> where it fails.

Would it be possible to write a simple C test that can be run on the different platforms in order to check the behavior of lseek?

Note that I'll be off the hook for the next ten days , so don't expect feedback before the beginning of next year.
Comment 41 tkoenig@netcologne.de 2012-12-23 15:09:59 UTC
Am 23.12.2012 15:16, schrieb dominiq at lps dot ens.fr:
>
> http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30162
>
> --- Comment #40 from Dominique d'Humieres <dominiq at lps dot ens.fr> 2012-12-23 14:16:52 UTC ---
>> Unfortunately, I cannot really debug this without access to a machine
>> where it fails.
>
> Would it be possible to write a simple C test that can be run on the different
> platforms in order to check the behavior of lseek?

The tests that you ran with the patchlet show that the error is not
in setting errno on the lseek.  It has to be somewhere else.

If you still have the time, could you attach the truss output of

program main
   open (10,file="pipe",status="old")
   write (10,'(A)') 'Hello, world!'
end program main

where pipe is a FIFO, and the same for

program read
   character(len=100) c
   open (10,file="pipe", status="old")
   read (10,'(A)') c
   print *,trim(c)
end program read

(first starting read, then write).
Comment 42 Thomas Koenig 2012-12-25 15:26:24 UTC
I'll try to find a system I have access to where this also fails;
unassigning myself until then.
Comment 43 Dominique d'Humieres 2013-02-17 20:10:52 UTC
I finally had a look at this PR.
First the test on comment #41 works on x86_64-apple-darwin10 r196109:

macbook] f90/bug% mkfifo pipe
[macbook] f90/bug% read_f &
[1] 6998
[macbook] f90/bug% write_f
 Hello, world!
[macbook] f90/bug% 
[1]    Done                          read_f

I have done the following changes to the original test:

[macbook] f90/bug% cat pr30162_1_db.f
      integer status
      integer i
      open(unit=20,file='np',action='write',
     &     form='unformatted',iostat=status);
      print*,'status for write from open is ',status
      do i = 1,5
         print *, "write ", i
         write(20)i
      end do
      end
[macbook] f90/bug% cat pr30162_2_db.f
      integer status
      integer i
      integer val
      open(unit=21,file='np',action='read',
     &     form='unformatted',iostat=status);
      print*,'status for read from open is ',status
      do i = 1,5
         read(21)val
         print*,val
      end do
      end

and I got

[macbook] f90/bug% write_f
 status for write from open is            0
 write            1
At line 8 of file pr30162_1_db.f (unit = 20, file = 'np')
Fortran runtime error: Illegal seek
 status for read from open is            0
At line 8 of file pr30162_2_db.f (unit = 21, file = 'np')
Fortran runtime error: I/O past end of record on unformatted file
[macbook] f90/bug% 
[1]    Exit 2                        read_f

Then I have applied the following patch (based on r194679):

--- ../_clean/libgfortran/io/unix.c	2013-01-14 19:25:10.000000000 +0100
+++ libgfortran/io/unix.c	2013-02-17 20:23:43.000000000 +0100
@@ -336,7 +336,14 @@ raw_write (unix_stream * s, const void *
 static gfc_offset
 raw_seek (unix_stream * s, gfc_offset offset, int whence)
 {
-  return lseek (s->fd, offset, whence);
+  gfc_offset x;
+  x = lseek (s->fd, offset, whence);
+  /* Non-seekable files should always be assumed to be at
+     current position.  */
+  if (x == -1 && errno == ESPIPE)
+    x = 0;
+  return x;
+  /* return lseek (s->fd, offset, whence); */
 }
 
 static gfc_offset

and now I get

[macbook] f90/bug% write_f
 status for write from open is            0
 write            1
 write            2
 write            3
 write            4
 write            5
 status for read from open is            0
At line 8 of file pr30162_2_db.f (unit = 21, file = 'np')
Fortran runtime error: I/O past end of record on unformatted file
[macbook] f90/bug% 
[1]    Exit 2                        read_f

So the write seems now OK. For the read, dtrace gives

fstat64(0x3, 0x7FFF5FBFD580, 0x50)		 = 0 0
fstat64(0x3, 0x7FFF5FBFD5C0, 0x0)		 = 0 0
lseek(0x3, 0x0, 0x1)		 = -1 Err#29
write(0x1, " status for read from open is            0\n\0", 0x2B)		 = 43 0
read(0x3, "\0", 0x4)		 = 4 0
read(0x3, "\0", 0x0)		 = 0 0
lseek(0x3, 0x4, 0x1)		 = -1 Err#29
write(0x2, "At line 8 of file pr30162_2_db.f (unit = 21, file = 'np')\n\0", 0x3A)		 = 58 0
write(0x2, "Fortran runtime error: \0", 0x17)		 = 23 0
write(0x2, "I/O past end of record on unformatted file\0", 0x2A)		 = 42 0
write(0x2, "\n\0", 0x1)		 = 1 0
close(0x3)		 = 0 0

i.e., 2 errors #29 on lseek. I did not find out how this is connected to the error "I/O past end of record on unformatted file".
Comment 44 Thomas Koenig 2013-02-18 18:44:02 UTC
If unformatted sequential ever worked, it was by chance.

Look at this piece of code:

  /* Seek to the head and overwrite the bogus length with the real
     length.  */

  if (unlikely (sseek (dtp->u.p.current_unit->s, - m - 2 * record_marker, 
		       SEEK_CUR) < 0))
    goto io_error;

This works if it happens within a buffer, but you cannot rely on that.

I would therefore suggest to resolve this PR by issuing a well-defined
error if we encounter a pipe on opening.

I'll prepare a patch.
Comment 45 Dominique d'Humieres 2013-02-18 19:15:43 UTC
> ...
> I would therefore suggest to resolve this PR by issuing a well-defined
> error if we encounter a pipe on opening.

Are you suggesting to "fix" the non working named pipes on Darwin by removing them everywhere?
If yes, isn't it a little bit too radical?

If there are pieces of code working only by chance, would not it be better to fix them?
Comment 46 Janne Blomqvist 2013-02-18 20:16:28 UTC
(In reply to comment #44)
> If unformatted sequential ever worked, it was by chance.
> 
> Look at this piece of code:
> 
>   /* Seek to the head and overwrite the bogus length with the real
>      length.  */
> 
>   if (unlikely (sseek (dtp->u.p.current_unit->s, - m - 2 * record_marker, 
>                SEEK_CUR) < 0))
>     goto io_error;
> 
> This works if it happens within a buffer, but you cannot rely on that.
> 
> I would therefore suggest to resolve this PR by issuing a well-defined
> error if we encounter a pipe on opening.
> 
> I'll prepare a patch.

As I explained in comment #23, this is already handled. When opening a file, we stat() the fd, and use the buffered I/O functions only if it's a regular file, otherwise the unbuffered raw I/O functions are used.
Comment 47 tkoenig@netcologne.de 2013-02-18 21:24:31 UTC
Am 18.02.2013 21:16, schrieb jb at gcc dot gnu.org:
>> Look at this piece of code:
>> >
>> >   /* Seek to the head and overwrite the bogus length with the real
>> >      length.  */
>> >
>> >   if (unlikely (sseek (dtp->u.p.current_unit->s, - m - 2 * record_marker,
>> >                SEEK_CUR) < 0))
>> >     goto io_error;
>> >
>> >This works if it happens within a buffer, but you cannot rely on that.
>> >
>> >I would therefore suggest to resolve this PR by issuing a well-defined
>> >error if we encounter a pipe on opening.
>> >
>> >I'll prepare a patch.
> As I explained in comment #23, this is already handled. When opening a file, we
> stat() the fd, and use the buffered I/O functions only if it's a regular file,
> otherwise the unbuffered raw I/O functions are used.

This is part of the precipitate, not part of the solution ;-)

For unformatted sequential, we first write a dummy record marker, write
the data and the final record marker, then seek to the first marker
and re-write that. Of course, this fails for an unbuffered fifo.

If we want this to work, we should make sure we always use
_buffered_ I/O for unformatted sequential, setting a maximum
record length on open that we can handle with our buffer.
Comment 48 tkoenig@netcologne.de 2013-02-18 21:29:22 UTC
Am 18.02.2013 20:15, schrieb dominiq at lps dot ens.fr:
> Are you suggesting to "fix" the non working named pipes on Darwin by removing
> them everywhere?

Actually, the error happens everywhere, I could reproduce the error with
unformatted sequential on my Linux box.

> If yes, isn't it a little bit too radical?

Formatted sequential, unformatted stream and formatted stream
would continue to work. As BACKSPACE most certainly will not work
on a pipe, I don't see the advantage of using unformatted sequential.
Comment 49 Thomas Koenig 2013-02-18 21:46:58 UTC
Here's a proof of concept patch which lets the
test cases "work" due to buffering.

Index: unix.c
===================================================================
--- unix.c      (Revision 195922)
+++ unix.c      (Arbeitskopie)
@@ -995,7 +995,8 @@
   s->file_length = statbuf.st_size;
 
   /* Only use buffered IO for regular files.  */
-  if (S_ISREG (statbuf.st_mode)
+  if ((S_ISREG (statbuf.st_mode)
+       || S_ISFIFO (statbuf.st_mode))
       && !options.all_unbuffered
       && !(options.unbuffered_preconnected && 
           (s->fd == STDIN_FILENO 

What it doesn't do is handle writes larger than
BUFSIZE, and this limitation is not communicated up
from the stream library to the record length, and also
not the other way.  This would break the abstraction
(but maybe we would not care, would we?)
Comment 50 Janne Blomqvist 2013-02-19 10:59:30 UTC
(In reply to comment #47)
> Am 18.02.2013 21:16, schrieb jb at gcc dot gnu.org:
> >> Look at this piece of code:
> >> >
> >> >   /* Seek to the head and overwrite the bogus length with the real
> >> >      length.  */
> >> >
> >> >   if (unlikely (sseek (dtp->u.p.current_unit->s, - m - 2 * record_marker,
> >> >                SEEK_CUR) < 0))
> >> >     goto io_error;
> >> >
> >> >This works if it happens within a buffer, but you cannot rely on that.
> >> >
> >> >I would therefore suggest to resolve this PR by issuing a well-defined
> >> >error if we encounter a pipe on opening.
> >> >
> >> >I'll prepare a patch.
> > As I explained in comment #23, this is already handled. When opening a file, we
> > stat() the fd, and use the buffered I/O functions only if it's a regular file,
> > otherwise the unbuffered raw I/O functions are used.
> 
> This is part of the precipitate, not part of the solution ;-)
> 
> For unformatted sequential, we first write a dummy record marker, write
> the data and the final record marker, then seek to the first marker
> and re-write that. Of course, this fails for an unbuffered fifo.
> 
> If we want this to work, we should make sure we always use
> _buffered_ I/O for unformatted sequential, setting a maximum
> record length on open that we can handle with our buffer.

The buffered I/O functions generally assume that the underlying fd is a regular file; if you break that be prepared to handle a bunch of subtle fallout. I tried playing that whack-a-mole game a few years ago until I got tired of it and restricted buffered I/O to regular files.

Wrt to pipes, one regression we had (I don't remember the PR number) was a deadlock due to the buffering. E.g. one process writes "hello" to the pipe, the other one answers "hello, neighbor". The write part could be handled by executing the FLUSH statement, but I recall there was some problem on the read side as well, something to do with read() hanging due to there not being any data available in the pipe when it tried to fill the buffer.

Anyway, I don't think this is something libgfortran can, or should, try to fix. If the user tries to do something requiring seeking on a special file that doesn't support seeking, they get what they deserve. The real bug here is that we try to seek even though the Fortran semantics don't require it (mind that the complaint in comment #22 which reopened this PR refers to formatted I/O).
Comment 51 Janne Blomqvist 2013-02-19 22:48:50 UTC
(In reply to comment #22)
> Revision 180701 removed all checks for special files in unit.c:unit_truncate().
> As a consequence programs compiled with gfortran 4.6.3 and newer cannot to
> write (formatted) to Unix named pipes as ftruncate() fails with EINVAL:
> 
> (64-bit code)
> 
> lseek(3, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
> write(3, " Hello, world!\n", 15)        = 15
> lseek(3, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
> ftruncate(3, 18446744073709551615)      = -1 EINVAL (Invalid argument)
> 
> (32-bit code)
> 
> _llseek(3, 0, 0xffba8fa0, SEEK_CUR)     = -1 ESPIPE (Illegal seek)
> write(3, " Hello, world!\n", 15)        = 15
> _llseek(3, 0, 0xffba9080, SEEK_CUR)     = -1 ESPIPE (Illegal seek)
> ftruncate64(3, 18446744073709551615)    = -1 EINVAL (Invalid argument)

This bug (http://stackoverflow.com/questions/11780556/write-to-fifo-named-pipe , presumably) should be fixed by the patch at

http://gcc.gnu.org/ml/gcc-patches/2013-02/msg00923.html
Comment 52 Janne Blomqvist 2013-02-21 19:03:19 UTC
Author: jb
Date: Thu Feb 21 19:03:10 2013
New Revision: 196210

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=196210
Log:
Fix regression when writing formatted sequential to a pipe.

2013-02-21  Janne Blomqvist  <jb@gcc.gnu.org>

	PR libfortran/30162
	* io/open.c (test_endfile): Call stell only if size != 0.
	* io/unix.c (raw_tell): Revert r194679.
	(raw_size): Return size field only for regular files, otherwise 0.

Modified:
    trunk/libgfortran/ChangeLog
    trunk/libgfortran/io/open.c
    trunk/libgfortran/io/unix.c
Comment 53 Janne Blomqvist 2013-02-21 20:13:12 UTC
Author: jb
Date: Thu Feb 21 20:13:04 2013
New Revision: 196212

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=196212
Log:
Fix regression when writing formatted sequential to a pipe.

2013-02-21  Janne Blomqvist  <jb@gcc.gnu.org>

	PR libfortran/30162
	* io/open.c (test_endfile): Call stell only if size != 0.
	* io/unix.c (raw_tell): Revert r194694.
	(raw_size): Return size field only for regular files, otherwise 0.

Modified:
    branches/gcc-4_7-branch/libgfortran/ChangeLog
    branches/gcc-4_7-branch/libgfortran/io/open.c
    branches/gcc-4_7-branch/libgfortran/io/unix.c
Comment 54 Janne Blomqvist 2013-02-21 20:39:58 UTC
I'd suggest to close the unformatted sequential part of this PR as WONTFIX. Maybe it worked sometime in the past for small record sizes when buffered IO was used for pipes, but that was by accident not by design. I think re-enabling buffering for pipes would open up a can of worms for very little benefit.

A way to "properly" enable this would be to rework the IO library so that we know how much we're writing before writing the initial record marker (so that we don't need to seek back and fill in the correct value later), however that implies a rather major rework of how we do IO, so IMHO the benefit is not worth it.
Comment 55 Tobias Burnus 2013-02-22 08:54:30 UTC
(In reply to comment #54)
> I'd suggest to close the unformatted sequential part of this PR as WONTFIX. [...]

Should we document in http://gcc.gnu.org/onlinedocs/gfortran/ that I/O via pipes (and other nonseekable devices) is only supported for STREAM and for FORMATTED+SEQUENTIAL?
Comment 56 Richard Biener 2013-04-11 07:58:51 UTC
GCC 4.7.3 is being released, adjusting target milestone.
Comment 57 Thomas Koenig 2013-08-09 09:25:15 UTC
Downgrading to a documentation issue.
Comment 58 Jerry DeLisle 2015-02-01 13:20:26 UTC
Closing.