[PATCH 13/14] Cygwin: FIFO: improve raw_write
Ken Brown
kbrown@cornell.edu
Sun Apr 14 19:16:00 GMT 2019
Don't set the write end of the pipe to non-blocking mode if the FIFO
is opened in blocking mode.
In fhandler_fifo::raw_write in blocking mode, wait for the write to
complete rather than returning -1 with EAGAIN.
If the amount to write is large, write in smaller chunks (of size
determined by a new data member max_atomic_write), as in
fhandler_base_overlapped.
---
winsup/cygwin/fhandler.h | 1 +
winsup/cygwin/fhandler_fifo.cc | 96 +++++++++++++++++++++++++++-------
2 files changed, 79 insertions(+), 18 deletions(-)
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index aea02c2b3..fd205a6db 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -1273,6 +1273,7 @@ class fhandler_fifo: public fhandler_base
int nhandlers, nconnected;
af_unix_spinlock_t _fifo_client_lock;
bool reader, writer, duplexer;
+ size_t max_atomic_write;
bool __reg2 wait (HANDLE);
NTSTATUS npfs_handle (HANDLE &);
HANDLE create_pipe_instance (bool);
diff --git a/winsup/cygwin/fhandler_fifo.cc b/winsup/cygwin/fhandler_fifo.cc
index fe4c67bdf..ce078d74d 100644
--- a/winsup/cygwin/fhandler_fifo.cc
+++ b/winsup/cygwin/fhandler_fifo.cc
@@ -22,6 +22,9 @@
#include "ntdll.h"
#include "cygwait.h"
+#define STATUS_THREAD_SIGNALED ((NTSTATUS)0xE0000001)
+#define STATUS_THREAD_CANCELED ((NTSTATUS)0xE0000002)
+
/* This is only to be used for writers. When reading,
STATUS_PIPE_EMPTY simply means there's no data to be read. */
#define STATUS_PIPE_IS_CLOSED(status) \
@@ -39,7 +42,8 @@ STATUS_PIPE_EMPTY simply means there's no data to be read. */
fhandler_fifo::fhandler_fifo ():
fhandler_base (), read_ready (NULL), write_ready (NULL),
listen_client_thr (NULL), lct_termination_evt (NULL), nhandlers (0),
- nconnected (0), reader (false), writer (false), duplexer (false)
+ nconnected (0), reader (false), writer (false), duplexer (false),
+ max_atomic_write (DEFAULT_PIPEBUFSIZE)
{
pipe_name_buf[0] = L'\0';
need_fork_fixup (true);
@@ -559,7 +563,7 @@ fhandler_fifo::open (int flags, mode_t)
NTSTATUS status = open_pipe ();
if (NT_SUCCESS (status))
{
- set_pipe_non_blocking (get_handle (), true);
+ set_pipe_non_blocking (get_handle (), flags & O_NONBLOCK);
if (!arm (write_ready))
res = error_set_errno;
else
@@ -661,28 +665,84 @@ ssize_t __reg3
fhandler_fifo::raw_write (const void *ptr, size_t len)
{
ssize_t ret = -1;
+ size_t nbytes = 0, chunk;
NTSTATUS status;
IO_STATUS_BLOCK io;
+ HANDLE evt = NULL;
- status = NtWriteFile (get_handle (), NULL, NULL, NULL, &io,
- (PVOID) ptr, len, NULL, NULL);
- if (NT_SUCCESS (status))
+ if (len <= max_atomic_write)
+ chunk = len;
+ else if (is_nonblocking ())
+ chunk = len = max_atomic_write;
+ else
+ chunk = max_atomic_write;
+
+ /* Create a wait event if the FIFO is in blocking mode. */
+ if (!is_nonblocking () && !(evt = CreateEvent (NULL, false, false, NULL)))
+ return -1;
+
+ /* Write in chunks, accumulating a total. If there's an error, just
+ return the accumulated total unless the first write fails, in
+ which case return -1. */
+ while (nbytes < len)
{
- /* NtWriteFile returns success with # of bytes written == 0 in
- case writing on a non-blocking pipe fails if the pipe buffer
- is full. */
- if (io.Information == 0)
- set_errno (EAGAIN);
+ ULONG_PTR nbytes_now = 0;
+ size_t left = len - nbytes;
+ size_t len1;
+ if (left > chunk)
+ len1 = chunk;
else
- ret = io.Information;
- }
- else if (STATUS_PIPE_IS_CLOSED (status))
- {
- set_errno (EPIPE);
- raise (SIGPIPE);
+ len1 = left;
+ nbytes_now = 0;
+ status = NtWriteFile (get_handle (), evt, NULL, NULL, &io,
+ (PVOID) ptr, len1, NULL, NULL);
+ if (evt && status == STATUS_PENDING)
+ {
+ DWORD waitret = cygwait (evt, cw_infinite, cw_cancel | cw_sig_eintr);
+ switch (waitret)
+ {
+ case WAIT_OBJECT_0:
+ status = io.Status;
+ break;
+ case WAIT_SIGNALED:
+ status = STATUS_THREAD_SIGNALED;
+ break;
+ case WAIT_CANCELED:
+ status = STATUS_THREAD_CANCELED;
+ break;
+ default:
+ break;
+ }
+ }
+ if (NT_SUCCESS (status))
+ {
+ nbytes_now = io.Information;
+ /* NtWriteFile returns success with # of bytes written == 0
+ if writing on a non-blocking pipe fails because the pipe
+ buffer doesn't have sufficient space. */
+ if (nbytes_now == 0)
+ set_errno (EAGAIN);
+ ptr = ((char *) ptr) + chunk;
+ nbytes += nbytes_now;
+ }
+ else if (STATUS_PIPE_IS_CLOSED (status))
+ {
+ set_errno (EPIPE);
+ raise (SIGPIPE);
+ }
+ else
+ __seterrno_from_nt_status (status);
+ if (nbytes_now == 0)
+ len = 0; /* Terminate loop. */
+ if (nbytes > 0)
+ ret = nbytes;
}
- else
- __seterrno_from_nt_status (status);
+ if (evt)
+ CloseHandle (evt);
+ if (status == STATUS_THREAD_SIGNALED && !_my_tls.call_signal_handler ())
+ set_errno (EINTR);
+ else if (status == STATUS_THREAD_CANCELED)
+ pthread::static_cancel_self ();
return ret;
}
--
2.17.0
More information about the Cygwin-patches
mailing list