Skip to content

Commit

Permalink
Cygwin: pipe: Adopt FILE_SYNCHRONOUS_IO_NONALERT flag for read pipe.
Browse files Browse the repository at this point in the history
- With this patch, FILE_SYNCHRONOUS_IO_NONALERT flag is applied to
  the read pipe so that native C# programs work with cygwin pipe.
  To handle signals in reading pipe with this flag set, the read
  pipe itself is always set to nonblocking mode and simulates the
  blocking behaviour in raw_read().

- Default to byte mode rather than message mode in order to make
  C# programs work with cygwin pipe.

Addresses: https://cygwin.com/pipermail/cygwin/2021-March/247987.html
  • Loading branch information
tyan0 committed Dec 12, 2021
1 parent f3d719c commit 9e4d308
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 46 deletions.
72 changes: 27 additions & 45 deletions winsup/cygwin/fhandler_pipe.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,10 @@ fhandler_pipe::init (HANDLE f, DWORD a, mode_t mode, int64_t uniq_id)
set_ino (uniq_id);
set_unique_id (uniq_id | !!(mode & GENERIC_WRITE));
if (opened_properly)
set_pipe_non_blocking (is_nonblocking ());
/* Set read pipe always nonblocking to allow signal handling
even with FILE_SYNCHRONOUS_IO_NONALERT. */
set_pipe_non_blocking (get_device () == FH_PIPER ?
true : is_nonblocking ());
return 1;
}

Expand Down Expand Up @@ -264,9 +267,9 @@ fhandler_pipe::release_select_sem (const char *from)
if (get_dev () == FH_PIPER) /* Number of select() and writer */
n_release = get_obj_handle_count (select_sem)
- get_obj_handle_count (read_mtx);
else /* Number of select() call */
else /* Number of select() call and reader */
n_release = get_obj_handle_count (select_sem)
- get_obj_handle_count (hdl_cnt_mtx);
- get_obj_handle_count (get_handle ());
debug_printf("%s(%s) release %d", from,
get_dev () == FH_PIPER ? "PIPER" : "PIPEW", n_release);
if (n_release)
Expand All @@ -279,7 +282,6 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
size_t nbytes = 0;
NTSTATUS status = STATUS_SUCCESS;
IO_STATUS_BLOCK io;
HANDLE evt;

if (!len)
return;
Expand Down Expand Up @@ -307,60 +309,27 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
len = (size_t) -1;
return;
}

if (!(evt = CreateEvent (NULL, false, false, NULL)))
{
__seterrno ();
len = (size_t) -1;
ReleaseMutex (read_mtx);
return;
}

while (nbytes < len)
{
ULONG_PTR nbytes_now = 0;
ULONG len1 = (ULONG) (len - nbytes);
waitret = WAIT_OBJECT_0;

ResetEvent (evt);
FILE_PIPE_LOCAL_INFORMATION fpli;
status = NtQueryInformationFile (get_handle (), &io,
&fpli, sizeof (fpli),
FilePipeLocalInformation);
if (NT_SUCCESS (status))
{
if (fpli.ReadDataAvailable == 0 && nbytes != 0)
break;
if (fpli.ReadDataAvailable == 0 && nbytes != 0)
break;
}
else if (nbytes != 0)
break;
status = NtReadFile (get_handle (), evt, NULL, NULL, &io, ptr,
status = NtReadFile (get_handle (), NULL, NULL, NULL, &io, ptr,
len1, NULL, NULL);
if (status == STATUS_PENDING)
{
waitret = cygwait (evt, INFINITE, cw_cancel | cw_sig);
/* If io.Status is STATUS_CANCELLED after CancelIo, IO has actually
been cancelled and io.Information contains the number of bytes
processed so far.
Otherwise IO has been finished regulary and io.Status contains
valid success or error information. */
CancelIo (get_handle ());
if (waitret == WAIT_SIGNALED && io.Status != STATUS_CANCELLED)
waitret = WAIT_OBJECT_0;

if (waitret == WAIT_CANCELED)
status = STATUS_THREAD_CANCELED;
else if (waitret == WAIT_SIGNALED)
status = STATUS_THREAD_SIGNALED;
else
status = io.Status;
}
if (isclosed ()) /* A signal handler might have closed the fd. */
{
if (waitret == WAIT_OBJECT_0)
set_errno (EBADF);
else
__seterrno ();
set_errno (EBADF);
nbytes = (size_t) -1;
}
else if (NT_SUCCESS (status)
Expand Down Expand Up @@ -393,7 +362,16 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
nbytes = (size_t) -1;
break;
}
fallthrough;
waitret = cygwait (select_sem, 1);
if (waitret == WAIT_CANCELED)
pthread::static_cancel_self ();
else if (waitret == WAIT_SIGNALED)
{
set_errno (EINTR);
nbytes = (size_t) -1;
break;
}
continue;
default:
__seterrno_from_nt_status (status);
nbytes = (size_t) -1;
Expand All @@ -406,7 +384,6 @@ fhandler_pipe::raw_read (void *ptr, size_t& len)
break;
}
ReleaseMutex (read_mtx);
CloseHandle (evt);
if (status == STATUS_THREAD_SIGNALED && nbytes == 0)
{
set_errno (EINTR);
Expand Down Expand Up @@ -990,9 +967,12 @@ nt_create (LPSECURITY_ATTRIBUTES sa_ptr, HANDLE &r, HANDLE &w,
npfsh, sa_ptr->lpSecurityDescriptor);

timeout.QuadPart = -500000;
/* Set FILE_SYNCHRONOUS_IO_NONALERT flag so that native
C# programs work with cygwin pipe. */
status = NtCreateNamedPipeFile (&r, access, &attr, &io,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_CREATE, 0, pipe_type,
FILE_CREATE,
FILE_SYNCHRONOUS_IO_NONALERT, pipe_type,
FILE_PIPE_BYTE_STREAM_MODE,
0, 1, psize, psize, &timeout);

Expand Down Expand Up @@ -1104,7 +1084,9 @@ fhandler_pipe::fcntl (int cmd, intptr_t arg)
const bool was_nonblocking = is_nonblocking ();
int res = fhandler_base::fcntl (cmd, arg);
const bool now_nonblocking = is_nonblocking ();
if (now_nonblocking != was_nonblocking)
/* Do not set blocking mode for read pipe to allow signal handling
even with FILE_SYNCHRONOUS_IO_NONALERT. */
if (now_nonblocking != was_nonblocking && get_device () != FH_PIPER)
set_pipe_non_blocking (now_nonblocking);
return res;
}
Expand Down
2 changes: 1 addition & 1 deletion winsup/cygwin/globals.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ int NO_COPY dynamically_loaded;
/* Some CYGWIN environment variable variables. */
bool allow_glob = true;
bool ignore_case_with_glob;
bool pipe_byte;
bool pipe_byte = true; /* Default to byte mode so that C# programs work. */
bool reset_com;
bool wincmdln;
winsym_t allow_winsymlinks = WSYM_default;
Expand Down

0 comments on commit 9e4d308

Please sign in to comment.