- The commit b531d6b0 introduced temporary_query_hdl() which uses
SystemHandleInformation. With this patch, ProcessHandleInformation
rather than SystemHandleInformation is used if it is available.
This request is faster, however, is only available since Windows 8,
therefore, SystemHandleInformation is used for Windows Vista and 7
as before.
- The commit f79a4611 introduced query_hdl, which is the read pipe
handle kept in the write pipe instance in order to determine if
the pipe is ready to write in select(). This implementation has
a potential risk that the write side fails to detect the closure
of the read side if more than one writer exists and one of them
is a non-cygwin process.
With this patch, the strategy of commit f79a4611 is used only if
the process is running as a service. For a normal process,
instead of keeping query_hdl in the write pipe instance, it is
retrieved temporarily when select() is called. Actually, we
want to use tenporary query_hdl for all processes, however, it
does not work for service processes due to OpenProcess()
failure.
- query_hdl and hdl_cnt_mtx are moved from fhandler_pipe_fifo to
fhandler_pipe. Then reader_closed() is changed to virtual and
overridden in fhandler_pipe.
- This patch fixes the race issue in the handle counting to detect
closure of read pipe, which is introduced by commit f79a4611.
A mutex hdl_cnt_mtx is introduced for this issue.
- Currently, raw_read(), raw_write() and close() release select_sem
unconditionally even if no waiter for select_sem exists. With this
patch, only the minimum number of semaphores required is released.
- Usually WriteQuotaAvailable retrieved by NtQueryInformationFile()
on the write side reflects the space available in the inbound buffer
on the read side. However, if a pipe read is currently pending,
WriteQuotaAvailable on the write side is decremented by the number
of bytes the read side is requesting. So it's possible (even likely)
that WriteQuotaAvailable is 0, even if the inbound buffer on the
read side is not full. This can lead to a deadlock situation:
The reader is waiting for data, but select on the writer side
assumes that no space is available in the read side inbound buffer.
Currently, to avoid this stuation, read() does not request larger
block than pipe size - 1. However, this mechanism does not take
effect if the reader side is non-cygwin app.
The only reliable information is available on the read side, so
fetch info from the read side via the pipe-specific query handle
(query_hdl) introduced.
If the query_hdl (read handle) is kept in write side, writer can
not detect closure of read pipe. Therefore, raw_write() counts
write handle and query_hdl. If they are equal, only the pairs of
write handle and query_hdl are alive. In this case, raw_write()
returns EPIPE and raises SIGPIPE.
- Nonblocking pipes (PIPE_NOWAIT) are not well handled by non-Cygwin
tools, so convert pipe handles to PIPE_WAIT handles when spawning
a non-Cygwin process.
We already fetched the correct SECURITY_ATTRIBUTES at the start of
fhandler_pipe::create, so using another SECURITY_ATTRIBUTES object for
the mutex and semaphore objects doesn't make much sense.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
select_sem gets created on the read side with inheritence settings
depending on the O_CLOEXEC flag. Then it gets duplicated to the write
side with unconditional inheritence. Fix that.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
Fold all code branches potentially having read or written data into
a single if branch, so signalling select_sem catches all cases.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
Rename fhandler_pipe_and_fifo::max_atomic_write to pipe_buf_size.
This reflect its actual meaning better. The fhandler_pipe_and_fifo
constructor initializes it to DEFAULT_PIPEBUFSIZE (== 64K), which is
the buffer size for the windows pipes created by fhandler_pipe and
fhandler_fifo. But if we inherit a stdio pipe handle from a
non-Cygwin process, the buffer size could be different.
To remedy this, add a method fhandler_pipe::set_pipe_buf_size that
queries the OS for the pipe buffer size, and use it in
dtable::init_std_file_from_handle.
In blocking mode, the underlying IO must always be terminated,
one way or the other, to make sure the application knows the exact
state after returning from the IO function. Therefore, always call
CancelIo in blocking mode.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
Just cancelling a thread doesn't cancel async IO started by this thread.
Fix this by returning from cygwait and calling CancelIo before canceling
self.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
- By guarding read with read_mtx, no more than one ReadFile can
be called simultaneously. So couting read handles is no longer
necessary.
- Make raw_read code as similar as possible to raw_write code.
This is a parent of fhandler_pipe and fhandler_fifo for code that is
common between the two classes. Currently it just contains
max_atomic_write and raw_write(). The latter is identical to what
used to be fhandler_pipe::raw_write().
FILE_PIPE_LOCAL_INFORMATION::WriteQuotaAvailable is unreliable.
Usually WriteQuotaAvailable on the write side reflects the space
available in the inbound buffer on the read side. However, if a
pipe read is currently pending, WriteQuotaAvailable on the write side
is decremented by the number of bytes the read side is requesting.
So it's possible (even likely) that WriteQuotaAvailable is 0, even
if the inbound buffer on the read side is not full. This can lead to
a deadlock situation: The reader is waiting for data, but select
on the writer side assumes that no space is available in the read
side inbound buffer.
This patch implements a workaround by never trying to read more than
half the buffer size blocking if the read buffer is empty. This first
cut tries to take the number of open readers into account by reducing
the amount of requested bytes accordingly.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
POSIX requires atomicity for non-blocking writes <= PIPE_BUF bytes
and writing of at least 1 byte if any buffer space is left.
Windows NtWriteFile returns STATUS_SUCCESS and "0 bytes written"
if the write doesn't match buffer space. Fix this discrepancy.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
The buffer pointer is incremented by "chunk", which is what we
typically try to write, but this isn't what actually got written.
Increment the buffer pointer by what we actually wrote, as returned
by NtWriteFile.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
The read handles of pipes created by CreateNamedPipe don't have
FILE_WRITE_ATTRIBUTES access unless the pipe is created with
PIPE_ACCESS_DUPLEX. This causes set_pipe_non_blocking to fail on such
handles. To fix this, add a helper function nt_create, which uses
NtCreateNamedPipeFile instead of CreateNamedPipe and gives us more
flexibility in setting access rights.
Use this helper function in fhandler_pipe::create (fhandler_pipe *[2],
unsigned, int), which is the version of fhandler_pipe::create used by
the pipe and pipe2 system calls.
For convenience, also add a static member function
fhandler_pipe::npfs_handle similar to those used by fhandler_fifo and
fhandler_socket_unix.
Add methods 'set_pipe_non_blocking' and 'fcntl' to keep the blocking
mode of the Windows pipe in sync with that of the fhandler_pipe
object. This applies to pipes created with the 'pipe' and 'pipe2'
system calls.
Previously fhandler_pipe was derived from fhandler_base_overlapped,
which we are going to remove in a future commit. Make minimal changes
so that the build still succeeds.
Analyzing the fhandler::copyto logic shows that the fhandler_base::reset
method was only called from copyto anyway.
Trying to convert reset to a protected method uncovered that the copyto
method is actually thought upside down from an object oriented POV.
Rather than calling copyto, manipulating the object given as parameter,
rename the method to copy_from, which manipulates the calling object
itself with data from the object given as parameter.
Eventually make reset a protected method and rename it to
_copy_from_reset_helper to clarify it's only called from copy_from.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
path_conv::reset_conv_handle is only called after fhandler::copyto
has been called. This duplicated the path_conv_handle if there was
one, so just setting the conv handle to NULL potentially produces a
handle leak. Replace reset_conv_handle calls with calls to
close_conv_handle and drop the reset_conv_handle method.
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>