-/* Copyright (C) 2002-2017 Free Software Foundation, Inc.
+/* Copyright (C) 2002-2020 Free Software Foundation, Inc.
Contributed by Andy Vaught
F2003 I/O support contributed by Jerry DeLisle
#include "io.h"
#include "unix.h"
+#include "async.h"
#include <limits.h>
#ifdef HAVE_UNISTD_H
{
int fd;
- if ((mode & R_OK) && (fd = open (path, O_RDONLY)) < 0)
- return -1;
- close (fd);
+ if (mode & R_OK)
+ {
+ if ((fd = open (path, O_RDONLY)) < 0)
+ return -1;
+ else
+ close (fd);
+ }
- if ((mode & W_OK) && (fd = open (path, O_WRONLY)) < 0)
- return -1;
- close (fd);
+ if (mode & W_OK)
+ {
+ if ((fd = open (path, O_WRONLY)) < 0)
+ return -1;
+ else
+ close (fd);
+ }
if (mode == F_OK)
{
/* Unix and internal stream I/O module */
-static const int BUFFER_SIZE = 8192;
+static const int FORMATTED_BUFFER_SIZE_DEFAULT = 8192;
+static const int UNFORMATTED_BUFFER_SIZE_DEFAULT = 128*1024;
typedef struct
{
gfc_offset file_length; /* Length of the file. */
char *buffer; /* Pointer to the buffer. */
+ ssize_t buffer_size; /* Length of the buffer. */
int fd; /* The POSIX file descriptor. */
int active; /* Length of valid bytes in the buffer */
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;
}
}
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)
&& raw_seek (s, new_logical, SEEK_SET) < 0)
return -1;
s->buffer_offset = s->physical_offset = new_logical;
- if (to_read <= BUFFER_SIZE/2)
+ if (to_read <= s->buffer_size/2)
{
- did_read = raw_read (s, s->buffer, BUFFER_SIZE);
+ did_read = raw_read (s, s->buffer, s->buffer_size);
if (likely (did_read >= 0))
{
s->physical_offset += did_read;
s->buffer_offset = s->logical_offset;
/* Does the data fit into the buffer? As a special case, if the
- buffer is empty and the request is bigger than BUFFER_SIZE/2,
+ buffer is empty and the request is bigger than s->buffer_size/2,
write directly. This avoids the case where the buffer would have
to be flushed at every write. */
- if (!(s->ndirty == 0 && nbyte > BUFFER_SIZE/2)
- && s->logical_offset + nbyte <= s->buffer_offset + BUFFER_SIZE
+ if (!(s->ndirty == 0 && nbyte > s->buffer_size/2)
+ && s->logical_offset + nbyte <= s->buffer_offset + s->buffer_size
&& s->buffer_offset <= s->logical_offset
&& s->buffer_offset + s->ndirty >= s->logical_offset)
{
the request is bigger than the buffer size, write directly
bypassing the buffer. */
buf_flush (s);
- if (nbyte <= BUFFER_SIZE/2)
+ if (nbyte <= s->buffer_size/2)
{
memcpy (s->buffer, buf, nbyte);
s->buffer_offset = s->logical_offset;
static int
buf_markeor (unix_stream *s)
{
- if (s->unbuffered || s->ndirty >= BUFFER_SIZE / 2)
+ if (s->unbuffered || s->ndirty >= s->buffer_size / 2)
return buf_flush (s);
return 0;
}
};
static int
-buf_init (unix_stream *s)
+buf_init (unix_stream *s, bool unformatted)
{
s->st.vptr = &buf_vtable;
- s->buffer = xmalloc (BUFFER_SIZE);
+ /* Try to guess a good value for the buffer size. For formatted
+ I/O, we use so many CPU cycles converting the data that there is
+ more sense in converving memory and especially cache. For
+ unformatted, a bigger block can have a large impact in some
+ environments. */
+
+ if (unformatted)
+ {
+ if (options.unformatted_buffer_size > 0)
+ s->buffer_size = options.unformatted_buffer_size;
+ else
+ s->buffer_size = UNFORMATTED_BUFFER_SIZE_DEFAULT;
+ }
+ else
+ {
+ if (options.formatted_buffer_size > 0)
+ s->buffer_size = options.formatted_buffer_size;
+ else
+ s->buffer_size = FORMATTED_BUFFER_SIZE_DEFAULT;
+ }
+
+ s->buffer = xmalloc (s->buffer_size);
return 0;
}
*********************************************************************/
char *
-mem_alloc_r (stream *strm, int *len)
+mem_alloc_r (stream *strm, size_t *len)
{
unix_stream *s = (unix_stream *) strm;
gfc_offset n;
return NULL;
n = s->buffer_offset + s->active - where;
- if (*len > n)
+ if ((gfc_offset) *len > n)
*len = n;
s->logical_offset = where + *len;
char *
-mem_alloc_r4 (stream *strm, int *len)
+mem_alloc_r4 (stream *strm, size_t *len)
{
unix_stream *s = (unix_stream *) strm;
gfc_offset n;
return NULL;
n = s->buffer_offset + s->active - where;
- if (*len > n)
+ if ((gfc_offset) *len > n)
*len = n;
s->logical_offset = where + *len;
char *
-mem_alloc_w (stream *strm, int *len)
+mem_alloc_w (stream *strm, size_t *len)
{
unix_stream *s = (unix_stream *)strm;
gfc_offset m;
gfc_char4_t *
-mem_alloc_w4 (stream *strm, int *len)
+mem_alloc_w4 (stream *strm, size_t *len)
{
unix_stream *s = (unix_stream *)strm;
gfc_offset m;
mem_read (stream *s, void *buf, ssize_t nbytes)
{
void *p;
- int nb = nbytes;
+ size_t nb = nbytes;
p = mem_alloc_r (s, &nb);
if (p)
mem_read4 (stream *s, void *buf, ssize_t nbytes)
{
void *p;
- int nb = nbytes;
+ size_t nb = nbytes;
p = mem_alloc_r4 (s, &nb);
if (p)
mem_write (stream *s, const void *buf, ssize_t nbytes)
{
void *p;
- int nb = nbytes;
+ size_t nb = nbytes;
p = mem_alloc_w (s, &nb);
if (p)
mem_write4 (stream *s, const void *buf, ssize_t nwords)
{
gfc_char4_t *p;
- int nw = nwords;
+ size_t nw = nwords;
p = mem_alloc_w4 (s, &nw);
if (p)
static int
mem_close (unix_stream *s)
{
- free (s);
-
+ if (s)
+ free (s);
return 0;
}
internal file */
stream *
-open_internal (char *base, int length, gfc_offset offset)
+open_internal (char *base, size_t length, gfc_offset offset)
{
unix_stream *s;
internal file */
stream *
-open_internal4 (char *base, int length, gfc_offset offset)
+open_internal4 (char *base, size_t length, gfc_offset offset)
{
unix_stream *s;
(s->fd == STDIN_FILENO
|| s->fd == STDOUT_FILENO
|| s->fd == STDERR_FILENO)))
- buf_init (s);
+ buf_init (s, unformatted);
else
{
if (unformatted)
{
s->unbuffered = true;
- buf_init (s);
+ buf_init (s, unformatted);
}
else
raw_init (s);
filename. */
int
-compare_file_filename (gfc_unit *u, const char *name, int len)
+compare_file_filename (gfc_unit *u, const char *name, gfc_charlen_type len)
{
struct stat st;
int ret;
id = id_from_path (path);
#endif
- __gthread_mutex_lock (&unit_lock);
+ LOCK (&unit_lock);
retry:
u = find_file0 (unit_root, FIND_FILE0_ARGS);
if (u != NULL)
if (! __gthread_mutex_trylock (&u->lock))
{
/* assert (u->closed == 0); */
- __gthread_mutex_unlock (&unit_lock);
+ UNLOCK (&unit_lock);
goto done;
}
inc_waiting_locked (u);
}
- __gthread_mutex_unlock (&unit_lock);
+ UNLOCK (&unit_lock);
if (u != NULL)
{
- __gthread_mutex_lock (&u->lock);
+ LOCK (&u->lock);
if (u->closed)
{
- __gthread_mutex_lock (&unit_lock);
- __gthread_mutex_unlock (&u->lock);
+ LOCK (&unit_lock);
+ UNLOCK (&u->lock);
if (predec_waiting_locked (u) == 0)
free (u);
goto retry;
return u;
if (u->s)
sflush (u->s);
- __gthread_mutex_unlock (&u->lock);
+ UNLOCK (&u->lock);
}
u = u->right;
}
gfc_unit *u;
int min_unit = 0;
- __gthread_mutex_lock (&unit_lock);
+ LOCK (&unit_lock);
do
{
u = flush_all_units_1 (unit_root, min_unit);
if (u != NULL)
inc_waiting_locked (u);
- __gthread_mutex_unlock (&unit_lock);
+ UNLOCK (&unit_lock);
if (u == NULL)
return;
- __gthread_mutex_lock (&u->lock);
+ LOCK (&u->lock);
min_unit = u->unit_number + 1;
if (u->closed == 0)
{
sflush (u->s);
- __gthread_mutex_lock (&unit_lock);
- __gthread_mutex_unlock (&u->lock);
+ LOCK (&unit_lock);
+ UNLOCK (&u->lock);
(void) predec_waiting_locked (u);
}
else
{
- __gthread_mutex_lock (&unit_lock);
- __gthread_mutex_unlock (&u->lock);
+ LOCK (&unit_lock);
+ UNLOCK (&u->lock);
if (predec_waiting_locked (u) == 0)
free (u);
}
string. */
const char *
-inquire_sequential (const char *string, int len)
+inquire_sequential (const char *string, gfc_charlen_type len)
{
struct stat statbuf;
suitable for direct access. Returns a C-style string. */
const char *
-inquire_direct (const char *string, int len)
+inquire_direct (const char *string, gfc_charlen_type len)
{
struct stat statbuf;
is suitable for formatted form. Returns a C-style string. */
const char *
-inquire_formatted (const char *string, int len)
+inquire_formatted (const char *string, gfc_charlen_type len)
{
struct stat statbuf;
is suitable for unformatted form. Returns a C-style string. */
const char *
-inquire_unformatted (const char *string, int len)
+inquire_unformatted (const char *string, gfc_charlen_type len)
{
return inquire_formatted (string, len);
}
suitable for access. */
static const char *
-inquire_access (const char *string, int len, int mode)
+inquire_access (const char *string, gfc_charlen_type len, int mode)
{
if (string == NULL)
return no;
suitable for READ access. */
const char *
-inquire_read (const char *string, int len)
+inquire_read (const char *string, gfc_charlen_type len)
{
return inquire_access (string, len, R_OK);
}
suitable for READ access. */
const char *
-inquire_write (const char *string, int len)
+inquire_write (const char *string, gfc_charlen_type len)
{
return inquire_access (string, len, W_OK);
}
suitable for read and write access. */
const char *
-inquire_readwrite (const char *string, int len)
+inquire_readwrite (const char *string, gfc_charlen_type len)
{
return inquire_access (string, len, R_OK | W_OK);
}