This patch adds a call to _commit() on _WIN32 for the FLUSH subroutine
and the FLUSH statement. It removes the _commit from gfortran's buf_flush.
Background:
* gfortran internally buffers the I/O, but it calls the nonbuffering
open/write/read functions (and not, e.g., fopen/fwrite/fread). On
Unix/Darwin/Linux system, the changes become immediately visible to all
processes after a "write()".
* On Windows, there seems to be a file-descriptor specific system buffer
such that the data only becomes available to other processes after
either a "_commit" or after closing the file.
* The Windows _commit() is a combination of a (system) buffer flush - as
a "write()" already implies on POSIX systems, but it also ensures that
the data ends up on the disk, which on POSIX systems is done via fsync().
Currently, _commit() is also called for the internal buf_flush, which
can happen very often and thus delays the I/O a lot (cf. PR 50016). With
this patch, it is only called when the user explicitly called FLUSH().
Contrary to non-_WIN32 systems, this also flushes to the hard disk,
which causes some slow down. However, as the user (should) only rarely
call the function, it should not pose a real performance issue.
The patch should ensure that the values can be read via other
file-descriptors within the same program or from other programs; on the
same time, it prevents excessive _commit() calling as it is done with
the current code.
An alternative would be not to call _commit() at all, leaving it to the
user. That would match the fsync() result on POSIX systems, but I think
having a file not available in the current status for other processes
causes more confusion that it helps. Thus, I think for FLUSH, one should
really make sure other processes get the right data.
The patch was build and regtested on x86-64-linux (where it should be a
noop).
OK for the trunk?
Can someone test it on MinGW/MinGW-64?
Tobias
PS: For the discussion, see
http://gcc.gnu.org/ml/fortran/2011-10/msg00079.html
PPS: I regard this patch as rather independent of INQUIRE even if the
issue first occurred with INQUIRE (PR 44698). Thus, it is independent
from whether one calls "stat()" for an open file or whether one uses a
different method like seek(SEEK_END) + tell() to obtain the file size.
2011-10-17 Tobias Burnus <bur...@net-b.de>
PR fortran/50016
* intrinsic.texi (FLUSH): Document that it calls _commit
on MinGW(-w64).
2011-10-17 Tobias Burnus <bur...@net-b.de>
PR fortran/50016
* io/file_pos.c (st_flush): Call _commit on MinGW(-w64).
* io/intrinsics.c (flush_i4, flush_i8): Ditto.
* io/unix.c (flush_all_units_1, flush_all_units): Ditto.
(buf_flush): Remove _commit call.
diff --git a/gcc/fortran/intrinsic.texi b/gcc/fortran/intrinsic.texi
index 11f87a5..1a0e930 100644
--- a/gcc/fortran/intrinsic.texi
+++ b/gcc/fortran/intrinsic.texi
@@ -4775,7 +4775,10 @@ that the data is committed to disk.
On POSIX systems, you can request that all data is transferred to the
storage device by calling the @code{fsync} function, with the POSIX file
descriptor of the I/O unit as argument (retrieved with GNU intrinsic
-@code{FNUM}). The following example shows how:
+@code{FNUM}). The following example shows how. On Windows (MinGW and
+MinGW-w64), the @code{FLUSH} subroutine and @code{FLUSH} statement always
+call @code{_commit} to ensure that other file descriptors see the current
+file status; @code{_commit} additionally flushes the data to the disk.
@smallexample
! Declare the interface for POSIX fsync function
diff --git a/libgfortran/io/file_pos.c b/libgfortran/io/file_pos.c
index b034f38..4efdc1f 100644
--- a/libgfortran/io/file_pos.c
+++ b/libgfortran/io/file_pos.c
@@ -452,6 +452,10 @@ st_flush (st_parameter_filepos *fpp)
fbuf_flush (u, u->mode);
sflush (u->s);
+#ifdef _WIN32
+ /* Without _commit, changes are not visible to other file descriptors. */
+ _commit (u->s->fd);
+#endif
unlock_unit (u);
}
else
diff --git a/libgfortran/io/intrinsics.c b/libgfortran/io/intrinsics.c
index f48bd77..eaf2e17 100644
--- a/libgfortran/io/intrinsics.c
+++ b/libgfortran/io/intrinsics.c
@@ -207,6 +207,11 @@ flush_i4 (GFC_INTEGER_4 *unit)
if (us != NULL)
{
sflush (us->s);
+#ifdef _WIN32
+ /* Without _commit, changes are not visible
+ to other file descriptors. */
+ _commit (u->s->fd);
+#endif
unlock_unit (us);
}
}
@@ -230,6 +235,11 @@ flush_i8 (GFC_INTEGER_8 *unit)
if (us != NULL)
{
sflush (us->s);
+#ifdef _WIN32
+ /* Without _commit, changes are not visible
+ to other file descriptors. */
+ _commit (u->s->fd);
+#endif
unlock_unit (us);
}
}
diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c
index 25cb559..a88e83b 100644
--- a/libgfortran/io/unix.c
+++ b/libgfortran/io/unix.c
@@ -443,10 +443,6 @@ buf_flush (unix_stream * s)
if (s->ndirty != 0)
return -1;
-#ifdef _WIN32
- _commit (s->fd);
-#endif
-
return 0;
}
@@ -1531,7 +1527,14 @@ flush_all_units_1 (gfc_unit *u, int min_unit)
if (__gthread_mutex_trylock (&u->lock))
return u;
if (u->s)
- sflush (u->s);
+ {
+ sflush (u->s);
+#ifdef _WIN32
+ /* Without _commit, changes are not visible to other
+ file descriptors. */
+ _commit (u->s->fd);
+#endif
+ }
__gthread_mutex_unlock (&u->lock);
}
u = u->right;
@@ -1562,6 +1565,11 @@ flush_all_units (void)
if (u->closed == 0)
{
sflush (u->s);
+#ifdef _WIN32
+ /* Without _commit, changes are not visible to other
+ file descriptors. */
+ _commit (u->s->fd);
+#endif
__gthread_mutex_lock (&unit_lock);
__gthread_mutex_unlock (&u->lock);
(void) predec_waiting_locked (u);