aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/os
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/os')
-rw-r--r--src/nvim/os/fileio.c45
-rw-r--r--src/nvim/os/fileio.h3
-rw-r--r--src/nvim/os/fs.c50
3 files changed, 77 insertions, 21 deletions
diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c
index a95adc86b6..ccf35fd57c 100644
--- a/src/nvim/os/fileio.c
+++ b/src/nvim/os/fileio.c
@@ -83,7 +83,7 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
if (fd < 0) {
return fd;
}
- return file_open_fd(ret_fp, fd, (wr == kTrue));
+ return file_open_fd(ret_fp, fd, flags);
}
/// Wrap file descriptor with FileDescriptor structure
@@ -94,14 +94,23 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
/// @param[out] ret_fp Address where information needed for reading from or
/// writing to a file is saved
/// @param[in] fd File descriptor to wrap.
-/// @param[in] wr True if fd is opened for writing only, false if it is read
-/// only.
+/// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and
+/// writing to the file at once is not supported, so either
+/// FILE_WRITE_ONLY or FILE_READ_ONLY is required.
///
/// @return Error code (@see os_strerror()) or 0. Currently always returns 0.
-int file_open_fd(FileDescriptor *const ret_fp, const int fd, const bool wr)
+int file_open_fd(FileDescriptor *const ret_fp, const int fd,
+ const int flags)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- ret_fp->wr = wr;
+ ret_fp->wr = !!(flags & (kFileCreate
+ |kFileCreateOnly
+ |kFileTruncate
+ |kFileAppend
+ |kFileWriteOnly));
+ ret_fp->non_blocking = !!(flags & kFileNonBlocking);
+ // Non-blocking writes not supported currently.
+ assert(!ret_fp->wr || !ret_fp->non_blocking);
ret_fp->fd = fd;
ret_fp->eof = false;
ret_fp->rv = rbuffer_new(kRWBufferSize);
@@ -138,15 +147,17 @@ FileDescriptor *file_open_new(int *const error, const char *const fname,
///
/// @param[out] error Error code, or 0 on success. @see os_strerror()
/// @param[in] fd File descriptor to wrap.
-/// @param[in] wr True if fd is opened for writing only, false if it is read
-/// only.
+/// @param[in] flags Flags, @see FileOpenFlags.
+/// @param[in] mode Permissions for the newly created file (ignored if flags
+/// does not have FILE_CREATE\*).
///
/// @return [allocated] Opened file or NULL in case of error.
-FileDescriptor *file_open_fd_new(int *const error, const int fd, const bool wr)
+FileDescriptor *file_open_fd_new(int *const error, const int fd,
+ const int flags)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
{
FileDescriptor *const fp = xmalloc(sizeof(*fp));
- if ((*error = file_open_fd(fp, fd, wr)) != 0) {
+ if ((*error = file_open_fd(fp, fd, flags)) != 0) {
xfree(fp);
return NULL;
}
@@ -244,7 +255,8 @@ static void file_rb_write_full_cb(RBuffer *const rv, FileDescriptor *const fp)
return;
}
const size_t read_bytes = rbuffer_read(rv, writebuf, kRWBufferSize);
- const ptrdiff_t wres = os_write(fp->fd, writebuf, read_bytes);
+ const ptrdiff_t wres = os_write(fp->fd, writebuf, read_bytes,
+ fp->non_blocking);
if (wres != (ptrdiff_t)read_bytes) {
if (wres >= 0) {
fp->_error = UV_EIO;
@@ -270,6 +282,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
char *buf = ret_buf;
size_t read_remaining = size;
RBuffer *const rv = fp->rv;
+ bool called_read = false;
while (read_remaining) {
const size_t rv_size = rbuffer_size(rv);
if (rv_size > 0) {
@@ -277,7 +290,9 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
buf += rsize;
read_remaining -= rsize;
}
- if (fp->eof) {
+ if (fp->eof
+ // Allow only at most one os_read[v] call.
+ || (called_read && fp->non_blocking)) {
break;
}
if (read_remaining) {
@@ -294,7 +309,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
};
assert(write_count == kRWBufferSize);
const ptrdiff_t r_ret = os_readv(fp->fd, &fp->eof, iov,
- ARRAY_SIZE(iov));
+ ARRAY_SIZE(iov), fp->non_blocking);
if (r_ret > 0) {
if (r_ret > (ptrdiff_t)read_remaining) {
rbuffer_produced(rv, (size_t)(r_ret - (ptrdiff_t)read_remaining));
@@ -310,7 +325,8 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
if (read_remaining >= kRWBufferSize) {
// …otherwise leave RBuffer empty and populate only target buffer,
// because filtering information through rbuffer will be more syscalls.
- const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining);
+ const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining,
+ fp->non_blocking);
if (r_ret >= 0) {
read_remaining -= (size_t)r_ret;
return (ptrdiff_t)(size - read_remaining);
@@ -321,7 +337,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
size_t write_count;
const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof,
rbuffer_write_ptr(rv, &write_count),
- kRWBufferSize);
+ kRWBufferSize, fp->non_blocking);
assert(write_count == kRWBufferSize);
if (r_ret > 0) {
rbuffer_produced(rv, (size_t)r_ret);
@@ -330,6 +346,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
}
}
#endif
+ called_read = true;
}
}
return (ptrdiff_t)(size - read_remaining);
diff --git a/src/nvim/os/fileio.h b/src/nvim/os/fileio.h
index 0b55cc695f..7c53cd4f07 100644
--- a/src/nvim/os/fileio.h
+++ b/src/nvim/os/fileio.h
@@ -14,6 +14,7 @@ typedef struct {
RBuffer *rv; ///< Read or write buffer.
bool wr; ///< True if file is in write mode.
bool eof; ///< True if end of file was encountered.
+ bool non_blocking; ///< True if EAGAIN should not restart syscalls.
} FileDescriptor;
/// file_open() flags
@@ -32,6 +33,8 @@ typedef enum {
///< kFileCreateOnly.
kFileAppend = 64, ///< Append to the file. Implies kFileWriteOnly. Cannot
///< be used with kFileCreateOnly.
+ kFileNonBlocking = 128, ///< Do not restart read() or write() syscall if
+ ///< EAGAIN was encountered.
} FileOpenFlags;
static inline bool file_eof(const FileDescriptor *const fp)
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index b7c2714296..0414794d01 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -436,6 +436,29 @@ int os_close(const int fd)
return r;
}
+/// Duplicate file descriptor
+///
+/// @param[in] fd File descriptor to duplicate.
+///
+/// @return New file descriptor or libuv error code (< 0).
+int os_dup(const int fd)
+ FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ int ret;
+os_dup_dup:
+ ret = dup(fd);
+ if (ret < 0) {
+ const int error = os_translate_sys_error(errno);
+ errno = 0;
+ if (error == UV_EINTR) {
+ goto os_dup_dup;
+ } else {
+ return error;
+ }
+ }
+ return ret;
+}
+
/// Read from a file
///
/// Handles EINTR and ENOMEM, but not other errors.
@@ -445,10 +468,11 @@ int os_close(const int fd)
/// to false. Initial value is ignored.
/// @param[out] ret_buf Buffer to write to. May be NULL if size is zero.
/// @param[in] size Amount of bytes to read.
+/// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered.
///
/// @return Number of bytes read or libuv error code (< 0).
-ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf,
- const size_t size)
+ptrdiff_t os_read(const int fd, bool *const ret_eof, char *const ret_buf,
+ const size_t size, const bool non_blocking)
FUNC_ATTR_WARN_UNUSED_RESULT
{
*ret_eof = false;
@@ -468,7 +492,9 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf,
if (cur_read_bytes < 0) {
const int error = os_translate_sys_error(errno);
errno = 0;
- if (error == UV_EINTR || error == UV_EAGAIN) {
+ if (non_blocking && error == UV_EAGAIN) {
+ break;
+ } else if (error == UV_EINTR || error == UV_EAGAIN) {
continue;
} else if (error == UV_ENOMEM && !did_try_to_free) {
try_to_free_memory();
@@ -498,7 +524,11 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf,
/// may change, it is incorrect to use data it points to after
/// os_readv().
/// @param[in] iov_size Number of buffers in iov.
-ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size)
+/// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered.
+///
+/// @return Number of bytes read or libuv error code (< 0).
+ptrdiff_t os_readv(const int fd, bool *const ret_eof, struct iovec *iov,
+ size_t iov_size, const bool non_blocking)
FUNC_ATTR_NONNULL_ALL
{
*ret_eof = false;
@@ -531,7 +561,9 @@ ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size)
} else if (cur_read_bytes < 0) {
const int error = os_translate_sys_error(errno);
errno = 0;
- if (error == UV_EINTR || error == UV_EAGAIN) {
+ if (non_blocking && error == UV_EAGAIN) {
+ break;
+ } else if (error == UV_EINTR || error == UV_EAGAIN) {
continue;
} else if (error == UV_ENOMEM && !did_try_to_free) {
try_to_free_memory();
@@ -551,9 +583,11 @@ ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size)
/// @param[in] fd File descriptor to write to.
/// @param[in] buf Data to write. May be NULL if size is zero.
/// @param[in] size Amount of bytes to write.
+/// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered.
///
/// @return Number of bytes written or libuv error code (< 0).
-ptrdiff_t os_write(const int fd, const char *const buf, const size_t size)
+ptrdiff_t os_write(const int fd, const char *const buf, const size_t size,
+ const bool non_blocking)
FUNC_ATTR_WARN_UNUSED_RESULT
{
if (buf == NULL) {
@@ -571,7 +605,9 @@ ptrdiff_t os_write(const int fd, const char *const buf, const size_t size)
if (cur_written_bytes < 0) {
const int error = os_translate_sys_error(errno);
errno = 0;
- if (error == UV_EINTR || error == UV_EAGAIN) {
+ if (non_blocking && error == UV_EAGAIN) {
+ break;
+ } else if (error == UV_EINTR || error == UV_EAGAIN) {
continue;
} else {
return error;