Bug 20179 - cannot mix C and Fortran I/O
Summary: cannot mix C and Fortran I/O
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libfortran (show other bugs)
Version: 4.0.0
: P2 normal
Target Milestone: 4.0.3
Assignee: Francois-Xavier Coudert
URL:
Keywords:
: 22106 23778 (view as bug list)
Depends on:
Blocks:
 
Reported: 2005-02-23 19:34 UTC by stevenj@fftw.org
Modified: 2005-10-30 13:49 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2005-05-25 11:10:59


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description stevenj@fftw.org 2005-02-23 19:34:35 UTC
When calling a C library from a gfortran-compiled program, C stdio is only
partially written.

Ability to mix languages as needed is really important in many practical
applications these days, so this would be nice to fix.  I suspect that
there is simply a missing fflush (or similar) somewhere, since the
problem appears when you have a printf with a missing newline (as in the
test case below) or when doing character-based (putc) I/O.

Environment:
System: Linux fftw.org 2.6.3-1-686-smp #2 SMP Tue Feb 24 20:29:08 EST 2004 i686 GNU/Linux
Architecture: i686

	
host: i686-pc-linux-gnu
build: i686-pc-linux-gnu
target: i686-pc-linux-gnu
configured with: ../configure --prefix=/home/stevenj/gcc4 --enable-languages=c,f95

How-To-Repeat:
Create two files, cio.c and ciotst.f, to be compiled in gcc and gfortran:

cio.c:
   #include <stdio.h>
   void cio_(void){
     printf("foo\n");
     printf("bar\n");
     printf("baz");
   }

ciotst.f:
      program ciotst
      call cio
      end

Compile:
	gcc -c cio.c
	gfortran ciotst.f cio.o -o ciotst

Run:
	./ciotst

Output:
foo
bar

Expected output (e.g. what I get with g77):
foo
bar
baz

(Note that last line is missing a newline.)
Comment 1 Andrew Pinski 2005-02-23 20:08:37 UTC
Werid.
Comment 2 Andrew Pinski 2005-02-23 20:18:29 UTC
Actually this looks more like mixing stdio functions and unix style functions (well mmap ones).
Comment 3 Thomas Koenig 2005-02-23 22:54:45 UTC
Add "fflush(stdout);" at the end of cio.c, and things work
as expected.
Comment 4 stevenj@fftw.org 2005-02-23 23:17:50 UTC
Subject: Re:  cannot mix C and Fortran I/O

On Wed, 23 Feb 2005, Thomas dot Koenig at online dot de wrote:
> Add "fflush(stdout);" at the end of cio.c, and things work
> as expected.

This is a workaround, but it would be nice to be able to mix C and Fortran 
code with I/O more seamlessly.  Is putting an fflush into a few strategic 
places in libgfortran that expensive that it overwhelms the benefit of 
seamless integration with stdio?
Comment 5 Tobias Schlüter 2005-02-27 18:18:50 UTC
We should probably call fflush() in the following places:
- at the beginning of a Fortran I/O operation
- at termination of the program

anywhere else?
Comment 6 Francois-Xavier Coudert 2005-05-22 22:09:34 UTC
As a reply to comment 2: this is indeed a problem of mixing unix-style and
C-style I/O, but not mmap ones (this was a different PR, fixed some time ago
now: mmap is not used on standard input, output and error).

Consider the following (program given in bug-report compiled as indicated):

$ ./a.out 
foo
bar
$ ./a.out | cat
$ GFORTRAN_STDOUT_UNIT=-1 ./a.out | cat
foo
bar
$ echo `./a.out`                        

$ echo `GFORTRAN_STDOUT_UNIT=-1 ./a.out`
foo bar baz
Comment 7 Francois-Xavier Coudert 2005-05-23 17:12:28 UTC
Good news for this PR. I located the root of the problem, which is that when the
library ends, we close() the stdout file descriptor, while the last line of
output (without newline trailing character) is not written yet.

So, the right thing to do might be not have a special test for stdout and
stderr, so that we don't close() those. Patch submitted for review.
Comment 8 stevenj 2005-05-24 23:51:17 UTC
I doubt that merely omitting close() from the end of the library will entirely
fix the problem.  You really need to add an fflush before every output to stdio.
 For example, modify ciotst.f to:

      program ciotst
      call cio
      write(*,*) 'Hello world.'
      call cio
      end

The g77 output is:

foo
bar
baz Hello world.
foo
bar
baz

as you would expect, but the gfortran output is in the wrong order (as well as
missing the final "baz" in my unpatched gfortran) because of missing flushes:

foo
bar
 Hello world.
bazfoo
bar

(If you look in libf2c you'll see that it uses fflush in a number of places.)
Comment 9 Francois-Xavier Coudert 2005-05-25 11:10:58 UTC
In the modified program, calls to fflush are missing, but they are missing from
the C routine. The library does not use FILE* streams and, as far as I know, it
does its own flushing right.

The problem you mention could be avoided by a preemptive fflush(stdout), but
this is only working around the user's code problems: when you use two different
systems of I/O, you should take care that each cleans up its internal mess
before switching to the other one. gfortran did not do this properly, hence the
patch I submitted. Now, my opinion is that in the case of the code from comment
#8, it's up to the user to write his code more carefully.

As an addition to this opinion (I'll be glad to hear any other point of view),
here is a small "state-of-the-art" on this point: Intel, IBM compilers do as I
suggest here, while Portland, MIPSpro and Sun compilers do as you expect.
Comment 10 stevenj 2005-05-25 16:47:56 UTC
It was already noted above that adding fflush to the user's code would fix the
output.  As a quality-of-implementation issue, however, I would suggest that
libgfortran should do it.

1) Interoperating well with code in other languages, and especially with C (the
de facto lingua franca) is increasingly important nowadays.

2) libc is the most common way to write to stdio.  Arguably, the onus is on code
using other libraries to interoperate well with it, rather than vice versa. 
(We're not talking about C code with some random home-brewed I/O library here.)

3) Fortran 2000 has a standard way to call C functions in libraries that may not
have been designed to be called from non-C languages, and it won't always be
practical to modify the C code to call fflush (e.g. the source may not even be
available).  Nor can the Fortran user herself easily call fflush(stdout),
because that call is not available (?) in Fortran.  Hence, if it is to be done
at all in such cases, it has to be done in libgfortran.

4) If the performance loss of calling fflush before outputting to stdio in
libgfortran is negligible (I have no data, just a guess), why is this even a
question?
Comment 11 Francois-Xavier Coudert 2005-05-25 18:37:27 UTC
In my view, interoperation  implies that every part of code leaves the system in
a clean state (I/O streams flushed, FPE flags in correct state) when execution
switches to another part of code. That is what the patch I mentionned is doing:
making sure gfortran doesn't stdout when some other code might still need it.

Now, it might be necessary to have special workarounds for the most common
problems (such as this one with stdout) that we can fix, even if we shouldn't
have to take care of. As always, the problem is: adding lots of ad hoc tricks
with "negligible" performance loss can result in big performance losses for
nothing in the most usual cases.

Anyway, I'll work on something. If you have some spare time (dreams, dreams), a
bit of performance-profiling could be useful.
Comment 12 GCC Commits 2005-05-30 07:38:53 UTC
Subject: Bug 20179

CVSROOT:	/cvs/gcc
Module name:	gcc
Changes by:	fxcoudert@gcc.gnu.org	2005-05-30 07:38:36

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

Log message:
	PR libfortran/20179
	* io/unix.c (fd_close): Add test so that we don't close()
	stdout and stderr.

Patches:
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libgfortran/ChangeLog.diff?cvsroot=gcc&r1=1.231&r2=1.232
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libgfortran/io/unix.c.diff?cvsroot=gcc&r1=1.26&r2=1.27

Comment 13 GCC Commits 2005-05-30 07:42:54 UTC
Subject: Bug 20179

CVSROOT:	/cvs/gcc
Module name:	gcc
Branch: 	gcc-4_0-branch
Changes by:	fxcoudert@gcc.gnu.org	2005-05-30 07:42:38

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

Log message:
	PR libfortran/20179
	* io/unix.c (fd_close): Add test so that we don't close()
	stdout and stderr.

Patches:
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libgfortran/ChangeLog.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=1.163.2.45&r2=1.163.2.46
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libgfortran/io/unix.c.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=1.21.10.4&r2=1.21.10.5

Comment 14 Francois-Xavier Coudert 2005-05-30 07:45:09 UTC
Patch for first part of the problem applied. Keeping open according to comments
#8 to #11.
Comment 15 Andrew Pinski 2005-06-17 18:12:14 UTC
*** Bug 22106 has been marked as a duplicate of this bug. ***
Comment 16 Francois-Xavier Coudert 2005-09-08 20:59:17 UTC
*** Bug 23778 has been marked as a duplicate of this bug. ***
Comment 17 Francois-Xavier Coudert 2005-09-10 16:06:37 UTC
Looking at that again, it is clear that there is a bug in the way libgfortran
handles pre-connected units. In short, the fd-based I/O library behaves like it
is the first program to access the file; this is not right for pre-connected
units. First example:

$ cat b.c    
extern void foo_(void);
int main ()
{
  write (1, "Hello world\n", 13);
  foo_ ();
  return 0;
}
$ cat b.f
      subroutine foo
      write (*,"(A)") "Ciao world!"
      end
$ ./b.out 
Hello world
Fortran runtime error: End of record


$ cat a.sh                                      
#!/bin/sh
${*} a.f
echo Before
./a.out
echo After
$ cat a.f 
      print *, "foo"
      end
$ ./a.sh ./debug/irun/bin/gfortran -static > foo && cat foo
 foo
After
$ ./a.sh ./debug/irun/bin/gfortran -static                 
Before
 foo
After

From the strace, the problem is quite clear: fd_truncate is called on the fd #1
(stdout), when the unit is opened.
Comment 18 Francois-Xavier Coudert 2005-09-10 16:16:24 UTC
I should add that the code concerned is in io/transfer.c, line 1174:

  /* Overwriting an existing sequential file ?
     it is always safe to truncate the file on the first write */
  if (g.mode == WRITING
      && current_unit->flags.access == ACCESS_SEQUENTIAL
      && current_unit->last_record == 0)
        struncate(current_unit->s);

Well, my opinion is that this comment is wrong! :)
Comment 19 GCC Commits 2005-09-11 13:35:03 UTC
Subject: Bug 20179

CVSROOT:	/cvs/gcc
Module name:	gcc
Changes by:	fxcoudert@gcc.gnu.org	2005-09-11 13:34:57

Modified files:
	libgfortran    : ChangeLog 
	libgfortran/io : io.h transfer.c unix.c 

Log message:
	PR libfortran/20179
	* io/unix.c (is_preconnected): Add function to test if a stream
	corresponds to a preconnected unit.
	* io/io.h: Add prototype for is_preconnected.
	* io/transfer.c (data_transfer_init): Do not truncate
	preconnected units.

Patches:
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libgfortran/ChangeLog.diff?cvsroot=gcc&r1=1.301&r2=1.302
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libgfortran/io/io.h.diff?cvsroot=gcc&r1=1.29&r2=1.30
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libgfortran/io/transfer.c.diff?cvsroot=gcc&r1=1.57&r2=1.58
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libgfortran/io/unix.c.diff?cvsroot=gcc&r1=1.38&r2=1.39

Comment 20 GCC Commits 2005-09-11 18:55:25 UTC
Subject: Bug 20179

CVSROOT:	/cvs/gcc
Module name:	gcc
Branch: 	gcc-4_0-branch
Changes by:	fxcoudert@gcc.gnu.org	2005-09-11 18:55:16

Modified files:
	gcc/testsuite  : ChangeLog 
	libgfortran    : ChangeLog 
	libgfortran/io : io.h transfer.c unix.c 
Added files:
	gcc/testsuite/gfortran.dg: overwrite_1.f 

Log message:
	PR libfortran/19872
	* gfortran.dg/overwrite_1.f: New test.
	
	PR libfortran/19872
	PR libfortran/20179
	* io/unix.c (is_preconnected): Add function to test if a stream
	corresponds to a preconnected unit.
	* io/io.h: Add prototype for is_preconnected.
	* io/transfer.c (data_transfer_init): Truncate overwritten files
	on first write, but not preconnected units.

Patches:
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/ChangeLog.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=1.5084.2.397&r2=1.5084.2.398
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/gfortran.dg/overwrite_1.f.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=NONE&r2=1.1.2.1
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libgfortran/ChangeLog.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=1.163.2.90&r2=1.163.2.91
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libgfortran/io/io.h.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=1.16.10.7&r2=1.16.10.8
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libgfortran/io/transfer.c.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=1.32.2.16&r2=1.32.2.17
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/libgfortran/io/unix.c.diff?cvsroot=gcc&only_with_tag=gcc-4_0-branch&r1=1.21.10.14&r2=1.21.10.15

Comment 21 T. Farago 2005-09-12 10:13:17 UTC
Ah great for fixing this, will try out as soon as the latest snapshot comes. I 
had the problem that was present in comment #17, just Fortran, no C-Fortran mix. 
It must have been some kind of regression (at least for libfortran) because it 
worked in the 4.0.0 release when we first tried it.
Comment 22 Francois-Xavier Coudert 2005-09-12 11:18:45 UTC
(In reply to comment #21)
> It must have been some kind of regression (at least for libfortran) because it 
> worked in the 4.0.0 release when we first tried it.

This bug was introduced while fixing another bug. This will now work on both
branches, including 4.0.2 (to be released soon).
Comment 23 Francois-Xavier Coudert 2005-10-29 12:44:16 UTC
Patch for that PR submitted (http://gcc.gnu.org/ml/fortran/2005-10/msg00682.html).
Comment 24 Francois-Xavier Coudert 2005-10-30 12:48:57 UTC
Subject: Bug 20179

Author: fxcoudert
Date: Sun Oct 30 12:48:52 2005
New Revision: 106017

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=106017
Log:
	PR libfortran/20179
	* io/unix.c (flush_if_preconnected): New function.
	* io/io.h: Add prototype for flush_if_preconnected.
	* io/transfer.c (data_transfer_init): Use flush_if_preconnected
	to workaround buggy mixed C-Fortran code.

	* gfortran.dg/mixed_io_1.f90: New test.
	* gfortran.dg/mixed_io_1.c: New file.

Added:
    trunk/gcc/testsuite/gfortran.dg/mixed_io_1.c
    trunk/gcc/testsuite/gfortran.dg/mixed_io_1.f90
Modified:
    trunk/gcc/testsuite/ChangeLog
    trunk/libgfortran/ChangeLog
    trunk/libgfortran/io/io.h
    trunk/libgfortran/io/transfer.c
    trunk/libgfortran/io/unix.c

Comment 25 Francois-Xavier Coudert 2005-10-30 13:25:30 UTC
Subject: Bug 20179

Author: fxcoudert
Date: Sun Oct 30 13:25:25 2005
New Revision: 106018

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=106018
Log:
	PR libfortran/20179
	* io/unix.c (flush_if_preconnected): New function.
	* io/io.h: Add prototype for flush_if_preconnected.
	* io/transfer.c (data_transfer_init): Use flush_if_preconnected
	to workaround buggy mixed C-Fortran code.

	* gfortran.dg/mixed_io_1.f90: New test.
	* gfortran.dg/mixed_io_1.c: New file.

Added:
    branches/gcc-4_0-branch/gcc/testsuite/gfortran.dg/mixed_io_1.c
      - copied unchanged from r106017, trunk/gcc/testsuite/gfortran.dg/mixed_io_1.c
    branches/gcc-4_0-branch/gcc/testsuite/gfortran.dg/mixed_io_1.f90
      - copied unchanged from r106017, trunk/gcc/testsuite/gfortran.dg/mixed_io_1.f90
Modified:
    branches/gcc-4_0-branch/gcc/testsuite/ChangeLog
    branches/gcc-4_0-branch/libgfortran/ChangeLog
    branches/gcc-4_0-branch/libgfortran/io/io.h
    branches/gcc-4_0-branch/libgfortran/io/transfer.c
    branches/gcc-4_0-branch/libgfortran/io/unix.c

Comment 26 Francois-Xavier Coudert 2005-10-30 13:49:27 UTC
Finally fixed by the last two commits (on both 4.0 and 4.1).