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.)
Werid.
Actually this looks more like mixing stdio functions and unix style functions (well mmap ones).
Add "fflush(stdout);" at the end of cio.c, and things work as expected.
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?
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?
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
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.
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.)
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.
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?
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.
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
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
Patch for first part of the problem applied. Keeping open according to comments #8 to #11.
*** Bug 22106 has been marked as a duplicate of this bug. ***
*** Bug 23778 has been marked as a duplicate of this bug. ***
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.
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! :)
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
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
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.
(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).
Patch for that PR submitted (http://gcc.gnu.org/ml/fortran/2005-10/msg00682.html).
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
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
Finally fixed by the last two commits (on both 4.0 and 4.1).