Many POSIX systems have the bad habit of not restarting interrupted syscalls. On these systems it's up to the user to check for an error with errno == EINTR and restart manually. This patch does this for libgfortran, so that GFortran users don't have to do it.
2016-10-07 Janne Blomqvist <j...@gcc.gnu.org> PR libfortran/67585 * io/unix.c (raw_read): Handle EINTR. (raw_write): Check for return value -1. (raw_seek): Handle EINTR. (raw_tell): Likewise. (raw_size): Likewise. (raw_truncate): Likewise. (raw_close): Likewise. (buf_flush): Call raw_seek instead of lseek. (buf_read): Likewise. (buf_write): Likewise. Regtested on x86_64-pc-linux-gnu. Ok for trunk? --- libgfortran/io/unix.c | 64 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c index 29818cd..43e33c8 100644 --- a/libgfortran/io/unix.c +++ b/libgfortran/io/unix.c @@ -298,8 +298,15 @@ 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. */ - return read (s->fd, buf, nbyte); + that will break applications that wait for interactive I/O. We + still can loop around EINTR, though. */ + while (true) + { + ssize_t trans = read (s->fd, buf, nbyte); + if (trans == -1 && errno == EINTR) + continue; + return trans; + } } static ssize_t @@ -316,7 +323,7 @@ raw_write (unix_stream * s, const void * buf, ssize_t nbyte) while (bytes_left > 0) { trans = write (s->fd, buf_st, bytes_left); - if (trans < 0) + if (trans == -1) { if (errno == EINTR) continue; @@ -333,22 +340,37 @@ raw_write (unix_stream * s, const void * buf, ssize_t nbyte) static gfc_offset raw_seek (unix_stream * s, gfc_offset offset, int whence) { - return lseek (s->fd, offset, whence); + while (true) + { + gfc_offset off = lseek (s->fd, offset, whence); + if (off == (gfc_offset) -1 && errno == EINTR) + continue; + return off; + } } static gfc_offset raw_tell (unix_stream * s) { - return lseek (s->fd, 0, SEEK_CUR); + while (true) + { + gfc_offset off = lseek (s->fd, 0, SEEK_CUR); + if (off == (gfc_offset) -1 && errno == EINTR) + continue; + return off; + } } static gfc_offset raw_size (unix_stream * s) { struct stat statbuf; - int ret = fstat (s->fd, &statbuf); - if (ret == -1) - return ret; + while (fstat (s->fd, &statbuf) == -1) + { + if (errno == EINTR) + continue; + return -1; + } if (S_ISREG (statbuf.st_mode)) return statbuf.st_size; else @@ -390,7 +412,13 @@ raw_truncate (unix_stream * s, gfc_offset length) lseek (s->fd, cur, SEEK_SET); return -1; #elif defined HAVE_FTRUNCATE - return ftruncate (s->fd, length); + while (ftruncate (s->fd, length) == -1) + { + if (errno == EINTR) + continue; + return -1; + } + return 0; #elif defined HAVE_CHSIZE return chsize (s->fd, length); #else @@ -409,7 +437,17 @@ raw_close (unix_stream * s) else if (s->fd != STDOUT_FILENO && s->fd != STDERR_FILENO && s->fd != STDIN_FILENO) - retval = close (s->fd); + { + retval = close (s->fd); + /* close() and EINTR is special, as the file descriptor is + deallocated before doing anything that might cause the + operation to be interrupted. Thus if we get EINTR the best we + can do is ignore it and continue (otherwise if we try again + the file descriptor may have been allocated again to some + other file). */ + if (retval == -1 && errno == EINTR) + retval = errno = 0; + } else retval = 0; free (s); @@ -463,7 +501,7 @@ buf_flush (unix_stream * s) return 0; if (s->physical_offset != s->buffer_offset - && lseek (s->fd, s->buffer_offset, SEEK_SET) < 0) + && raw_seek (s, s->buffer_offset, SEEK_SET) < 0) return -1; writelen = raw_write (s, s->buffer, s->ndirty); @@ -518,7 +556,7 @@ buf_read (unix_stream * s, void * buf, ssize_t nbyte) to_read = nbyte - nread; new_logical = s->logical_offset + nread; if (s->physical_offset != new_logical - && lseek (s->fd, new_logical, SEEK_SET) < 0) + && raw_seek (s, new_logical, SEEK_SET) < 0) return -1; s->buffer_offset = s->physical_offset = new_logical; if (to_read <= BUFFER_SIZE/2) @@ -587,7 +625,7 @@ buf_write (unix_stream * s, const void * buf, ssize_t nbyte) { if (s->physical_offset != s->logical_offset) { - if (lseek (s->fd, s->logical_offset, SEEK_SET) < 0) + if (raw_seek (s, s->logical_offset, SEEK_SET) < 0) return -1; s->physical_offset = s->logical_offset; } -- 2.7.4