Cygwin: FIFO: improve raw_write

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.

For convenience, add two new NTSTATUS codes, STATUS_THREAD_SIGNALED
and STATUS_THREAD_CANCELED, to ntdll.h.
This commit is contained in:
Ken Brown 2019-04-15 15:43:57 +00:00 committed by Corinna Vinschen
parent a7d08b3ecd
commit bb46627871
3 changed files with 82 additions and 22 deletions

View File

@ -1273,6 +1273,7 @@ class fhandler_fifo: public fhandler_base
int nhandlers, nconnected; int nhandlers, nconnected;
af_unix_spinlock_t _fifo_client_lock; af_unix_spinlock_t _fifo_client_lock;
bool reader, writer, duplexer; bool reader, writer, duplexer;
size_t max_atomic_write;
bool __reg2 wait (HANDLE); bool __reg2 wait (HANDLE);
NTSTATUS npfs_handle (HANDLE &); NTSTATUS npfs_handle (HANDLE &);
HANDLE create_pipe_instance (bool); HANDLE create_pipe_instance (bool);

View File

@ -39,7 +39,8 @@ STATUS_PIPE_EMPTY simply means there's no data to be read. */
fhandler_fifo::fhandler_fifo (): fhandler_fifo::fhandler_fifo ():
fhandler_base (), read_ready (NULL), write_ready (NULL), fhandler_base (), read_ready (NULL), write_ready (NULL),
listen_client_thr (NULL), lct_termination_evt (NULL), nhandlers (0), 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'; pipe_name_buf[0] = L'\0';
need_fork_fixup (true); need_fork_fixup (true);
@ -559,7 +560,7 @@ fhandler_fifo::open (int flags, mode_t)
NTSTATUS status = open_pipe (); NTSTATUS status = open_pipe ();
if (NT_SUCCESS (status)) if (NT_SUCCESS (status))
{ {
set_pipe_non_blocking (get_handle (), true); set_pipe_non_blocking (get_handle (), flags & O_NONBLOCK);
if (!arm (write_ready)) if (!arm (write_ready))
res = error_set_errno; res = error_set_errno;
else else
@ -661,28 +662,84 @@ ssize_t __reg3
fhandler_fifo::raw_write (const void *ptr, size_t len) fhandler_fifo::raw_write (const void *ptr, size_t len)
{ {
ssize_t ret = -1; ssize_t ret = -1;
NTSTATUS status; size_t nbytes = 0, chunk;
NTSTATUS status = STATUS_SUCCESS;
IO_STATUS_BLOCK io; IO_STATUS_BLOCK io;
HANDLE evt = NULL;
status = NtWriteFile (get_handle (), NULL, NULL, NULL, &io, if (len <= max_atomic_write)
(PVOID) ptr, len, NULL, NULL); chunk = len;
if (NT_SUCCESS (status)) else if (is_nonblocking ())
{ chunk = len = max_atomic_write;
/* 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);
else
ret = io.Information;
}
else if (STATUS_PIPE_IS_CLOSED (status))
{
set_errno (EPIPE);
raise (SIGPIPE);
}
else else
__seterrno_from_nt_status (status); 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)
{
ULONG_PTR nbytes_now = 0;
size_t left = len - nbytes;
size_t len1;
if (left > chunk)
len1 = chunk;
else
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;
}
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; return ret;
} }

View File

@ -15,7 +15,9 @@
extern GUID __cygwin_socket_guid; extern GUID __cygwin_socket_guid;
#define CYGWIN_SOCKET_GUID (&__cygwin_socket_guid) #define CYGWIN_SOCKET_GUID (&__cygwin_socket_guid)
/* custom status code: */ /* Custom Cygwin-only status codes. */
#define STATUS_THREAD_SIGNALED ((NTSTATUS)0xe0000001)
#define STATUS_THREAD_CANCELED ((NTSTATUS)0xe0000002)
#define STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION ((NTSTATUS) 0xe0000269) #define STATUS_ILLEGAL_DLL_PSEUDO_RELOCATION ((NTSTATUS) 0xe0000269)
/* Simplify checking for a transactional error code. */ /* Simplify checking for a transactional error code. */