gcc/libgfortran/io
Janne Blomqvist 8a159915b9 PR libgfortran/83649 Chunk large reads and writes
Backport from trunk.

It turns out that Linux never reads or writes more than 2147479552
bytes in a single syscall. For writes this is not a problem as
libgfortran already contains a loop around write() to handle short
writes. But for reads we cannot do this, since then read will hang if
we have a short read when reading from the terminal.  Also, there are
reports that macOS fails I/O's larger than 2 GB.  Thus, to work around
these issues do large reads/writes in chunks.

The testcase from the PR

program largewr
  integer(kind=1) :: a(2_8**31+1)
  a = 0
  a(size(a, kind=8)) = 1
  open(10, file="largewr.dat", access="stream", form="unformatted")
  write (10) a
  close(10)
  a(size(a, kind=8)) = 2
  open(10, file="largewr.dat", access="stream", form="unformatted")
  read (10) a
  if (a(size(a, kind=8)) == 1) then
    print *, "All is well"
  else
    print *, "Oh no"
  end if
end program largewr

fails on trunk but works with the patch.

Regtested on x86_64-pc-linux-gnu, committed to trunk.

libgfortran/ChangeLog:

2018-01-03  Janne Blomqvist  <jb@gcc.gnu.org>

	PR libgfortran/83649
	* io/unix.c (MAX_CHUNK): New define.
	(raw_read): For reads larger than MAX_CHUNK, loop.
	(raw_write): Write no more than MAX_CHUNK bytes per iteration.
---
 libgfortran/io/unix.c | 50 ++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 42 insertions(+), 8 deletions(-)

diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c
index a07a3c9..7a982b3 100644
--- a/libgfortran/io/unix.c
+++ b/libgfortran/io/unix.c
@@ -292,18 +292,49 @@ raw_flush (unix_stream *s  __attribute__ ((unused)))
   return 0;
 }
 
+/* Write/read at most 2 GB - 4k chunks at a time. Linux never reads or
+   writes more than this, and there are reports that macOS fails for
+   larger than 2 GB as well.  */
+#define MAX_CHUNK 2147479552
+
 static ssize_t
 raw_read (unix_stream *s, void *buf, ssize_t nbyte)
 {
   /* For read we can't do I/O in a loop like raw_write does, because
      that will break applications that wait for interactive I/O.  We
-     still can loop around EINTR, though.  */
-  while (true)
+     still can loop around EINTR, though.  This however causes a
+     problem for large reads which must be chunked, see comment above.
+     So assume that if the size is larger than the chunk size, we're
+     reading from a file and not the terminal.  */
+  if (nbyte <= MAX_CHUNK)
     {
-      ssize_t trans = read (s->fd, buf, nbyte);
-      if (trans == -1 && errno == EINTR)
-	continue;
-      return trans;
+      while (true)
+	{
+	  ssize_t trans = read (s->fd, buf, nbyte);
+	  if (trans == -1 && errno == EINTR)
+	    continue;
+	  return trans;
+	}
+    }
+  else
+    {
+      ssize_t bytes_left = nbyte;
+      char *buf_st = buf;
+      while (bytes_left > 0)
+	{
+	  ssize_t to_read = bytes_left < MAX_CHUNK ? bytes_left: MAX_CHUNK;
+	  ssize_t trans = read (s->fd, buf_st, to_read);
+	  if (trans == -1)
+	    {
+	      if (errno == EINTR)
+		continue;
+	      else
+		return trans;
+	    }
+	  buf_st += trans;
+	  bytes_left -= trans;
+	}
+      return nbyte - bytes_left;
     }
 }
 
@@ -317,10 +348,13 @@ raw_write (unix_stream *s, const void *buf, ssize_t nbyte)
   buf_st = (char *) buf;
 
   /* We must write in a loop since some systems don't restart system
-     calls in case of a signal.  */
+     calls in case of a signal.  Also some systems might fail outright
+     if we try to write more than 2 GB in a single syscall, so chunk
+     up large writes.  */
   while (bytes_left > 0)
     {
-      trans = write (s->fd, buf_st, bytes_left);
+      ssize_t to_write = bytes_left < MAX_CHUNK ? bytes_left: MAX_CHUNK;
+      trans = write (s->fd, buf_st, to_write);
       if (trans == -1)
 	{
 	  if (errno == EINTR)
-- 
2.7.4

From-SVN: r256172
2018-01-03 13:46:38 +02:00
..
close.c close.c: Fix white space in pointer declarations and comment formats where applicable. 2017-04-11 14:51:25 +00:00
fbuf.c close.c: Fix white space in pointer declarations and comment formats where applicable. 2017-04-11 14:51:25 +00:00
fbuf.h close.c: Fix white space in pointer declarations and comment formats where applicable. 2017-04-11 14:51:25 +00:00
file_pos.c backport: re PR fortran/80741 ([Regression 7/8] DTIO wrong code causes incorrect behaviour of namelist READ) 2017-05-23 22:05:56 +00:00
format.c Rimvydas (RJ) 2017-10-27 18:51:35 +00:00
format.h Update copyright years. 2017-01-01 13:07:43 +01:00
inquire.c backport: re PR libfortran/78549 (Very slow formatted internal file output) 2017-12-16 19:59:14 +00:00
intrinsics.c close.c: Fix white space in pointer declarations and comment formats where applicable. 2017-04-11 14:51:25 +00:00
io.h backport: re PR fortran/83225 (runtime error in transfer.c) 2017-12-03 05:05:51 +00:00
list_read.c backport: re PR libfortran/81937 (stack-buffer-overflow on memcpy in libgfortran/io/unix.c on character(kind=4)) 2017-12-16 21:57:17 +00:00
lock.c close.c: Fix white space in pointer declarations and comment formats where applicable. 2017-04-11 14:51:25 +00:00
open.c close.c: Fix white space in pointer declarations and comment formats where applicable. 2017-04-11 14:51:25 +00:00
read.c close.c: Fix white space in pointer declarations and comment formats where applicable. 2017-04-11 14:51:25 +00:00
size_from_kind.c Update copyright years. 2017-01-01 13:07:43 +01:00
transfer128.c Update copyright years. 2017-01-01 13:07:43 +01:00
transfer.c backport: re PR libfortran/78549 (Very slow formatted internal file output) 2017-12-16 19:59:14 +00:00
unit.c backport: re PR libfortran/83613 (Executing gfortran.dg/inquire_internal.f90 hangs on darwin after r255621) 2017-12-30 02:14:34 +00:00
unix.c PR libgfortran/83649 Chunk large reads and writes 2018-01-03 13:46:38 +02:00
unix.h close.c: Fix white space in pointer declarations and comment formats where applicable. 2017-04-11 14:51:25 +00:00
write_float.def Update copyright years. 2017-01-01 13:07:43 +01:00
write.c backport: re PR libfortran/83168 (FAIL: gfortran.dg/fmt_f0_2.f90 with a sanitized libgfortran) 2017-12-04 03:51:28 +00:00