From b9233944298c0975a7cab3b1053c3ea388e9fd15 Mon Sep 17 00:00:00 2001 From: Janne Blomqvist Date: Sun, 9 Oct 2016 21:05:56 +0300 Subject: [PATCH] PR 67585 Handle EINTR 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-09 Janne Blomqvist PR libfortran/67585 * io/io.h: TEMP_FAILURE_RETRY: Define macro if not found. * 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. (fd_to_stream): Handle EINTR. (tempfile_open): Likewise. (regular_file2): Likewise. (compare_file_filename): Likewise. (find_file): Likewise. (inquire_sequential): Likewise. (inquire_direct): Likewise. (inquire_formatted): Likewise. From-SVN: r240902 --- libgfortran/ChangeLog | 23 ++++++++++++ libgfortran/io/io.h | 15 ++++++++ libgfortran/io/unix.c | 86 ++++++++++++++++++++++++++++++------------- 3 files changed, 98 insertions(+), 26 deletions(-) diff --git a/libgfortran/ChangeLog b/libgfortran/ChangeLog index d38646ac8b7..62b52226090 100644 --- a/libgfortran/ChangeLog +++ b/libgfortran/ChangeLog @@ -1,3 +1,26 @@ +2016-10-09 Janne Blomqvist + + PR libfortran/67585 + * io/io.h: TEMP_FAILURE_RETRY: Define macro if not found. + * 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. + (fd_to_stream): Handle EINTR. + (tempfile_open): Likewise. + (regular_file2): Likewise. + (compare_file_filename): Likewise. + (find_file): Likewise. + (inquire_sequential): Likewise. + (inquire_direct): Likewise. + (inquire_formatted): Likewise. + 2016-10-05 Jerry DeLisle PR fortran/77868 diff --git a/libgfortran/io/io.h b/libgfortran/io/io.h index 87c35583754..ea93fbaf99d 100644 --- a/libgfortran/io/io.h +++ b/libgfortran/io/io.h @@ -660,6 +660,21 @@ typedef struct gfc_saved_unit } gfc_saved_unit; +/* TEMP_FAILURE_RETRY macro from glibc. */ + +#ifndef TEMP_FAILURE_RETRY +/* Evaluate EXPRESSION, and repeat as long as it returns -1 with `errno' + set to EINTR. */ + +# define TEMP_FAILURE_RETRY(expression) \ + (__extension__ \ + ({ long int __result; \ + do __result = (long int) (expression); \ + while (__result == -1L && errno == EINTR); \ + __result; })) +#endif + + /* unit.c */ /* Maximum file offset, computed at library initialization time. */ diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c index 29818cd7a14..1e84c42dd3a 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,33 @@ 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; + if (TEMP_FAILURE_RETRY (fstat (s->fd, &statbuf)) == -1) + return -1; if (S_ISREG (statbuf.st_mode)) return statbuf.st_size; else @@ -390,7 +408,9 @@ 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); + if (TEMP_FAILURE_RETRY (ftruncate (s->fd, length)) == -1) + return -1; + return 0; #elif defined HAVE_CHSIZE return chsize (s->fd, length); #else @@ -409,7 +429,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 +493,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 +548,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 +617,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; } @@ -1025,7 +1055,7 @@ fd_to_stream (int fd, bool unformatted) /* Get the current length of the file. */ - if (fstat (fd, &statbuf) == -1) + if (TEMP_FAILURE_RETRY (fstat (fd, &statbuf)) == -1) { s->st_dev = s->st_ino = -1; s->file_length = 0; @@ -1134,9 +1164,9 @@ tempfile_open (const char *tempdir, char **fname) #endif #if defined(HAVE_MKOSTEMP) && defined(O_CLOEXEC) - fd = mkostemp (template, O_CLOEXEC); + TEMP_FAILURE_RETRY (fd = mkostemp (template, O_CLOEXEC)); #else - fd = mkstemp (template); + TEMP_FAILURE_RETRY (fd = mkstemp (template)); set_close_on_exec (fd); #endif @@ -1178,7 +1208,7 @@ tempfile_open (const char *tempdir, char **fname) continue; } - fd = open (template, flags, S_IRUSR | S_IWUSR); + TEMP_FAILURE_RETRY (fd = open (template, flags, S_IRUSR | S_IWUSR)); } while (fd == -1 && errno == EEXIST); #ifndef O_CLOEXEC @@ -1355,7 +1385,7 @@ regular_file2 (const char *path, st_parameter_open *opp, unit_flags *flags) #endif mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; - fd = open (path, rwflag | crflag, mode); + TEMP_FAILURE_RETRY (fd = open (path, rwflag | crflag, mode)); if (flags->action != ACTION_UNSPECIFIED) return fd; @@ -1373,7 +1403,7 @@ regular_file2 (const char *path, st_parameter_open *opp, unit_flags *flags) crflag2 = crflag & ~(O_CREAT); else crflag2 = crflag; - fd = open (path, rwflag | crflag2, mode); + TEMP_FAILURE_RETRY (fd = open (path, rwflag | crflag2, mode)); if (fd >=0) { flags->action = ACTION_READ; @@ -1385,7 +1415,7 @@ regular_file2 (const char *path, st_parameter_open *opp, unit_flags *flags) /* retry for write-only access */ rwflag = O_WRONLY; - fd = open (path, rwflag | crflag, mode); + TEMP_FAILURE_RETRY (fd = open (path, rwflag | crflag, mode)); if (fd >=0) { flags->action = ACTION_WRITE; @@ -1512,7 +1542,7 @@ compare_file_filename (gfc_unit *u, const char *name, int len) /* If the filename doesn't exist, then there is no match with the * existing file. */ - if (stat (path, &st) < 0) + if (TEMP_FAILURE_RETRY (stat (path, &st)) < 0) { ret = 0; goto done; @@ -1614,7 +1644,7 @@ find_file (const char *file, gfc_charlen_type file_len) char *path = fc_strdup (file, file_len); - if (stat (path, &st[0]) < 0) + if (TEMP_FAILURE_RETRY (stat (path, &st[0])) < 0) { u = NULL; goto done; @@ -1742,7 +1772,8 @@ file_size (const char *file, gfc_charlen_type file_len) { char *path = fc_strdup (file, file_len); struct stat statbuf; - int err = stat (path, &statbuf); + int err; + TEMP_FAILURE_RETRY (err = stat (path, &statbuf)); free (path); if (err == -1) return -1; @@ -1764,7 +1795,8 @@ inquire_sequential (const char *string, int len) return unknown; char *path = fc_strdup (string, len); - int err = stat (path, &statbuf); + int err; + TEMP_FAILURE_RETRY (err = stat (path, &statbuf)); free (path); if (err == -1) return unknown; @@ -1792,7 +1824,8 @@ inquire_direct (const char *string, int len) return unknown; char *path = fc_strdup (string, len); - int err = stat (path, &statbuf); + int err; + TEMP_FAILURE_RETRY (err = stat (path, &statbuf)); free (path); if (err == -1) return unknown; @@ -1820,7 +1853,8 @@ inquire_formatted (const char *string, int len) return unknown; char *path = fc_strdup (string, len); - int err = stat (path, &statbuf); + int err; + TEMP_FAILURE_RETRY (err = stat (path, &statbuf)); free (path); if (err == -1) return unknown; -- 2.30.2