From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2155) id 70DA13858031; Tue, 14 Sep 2021 15:06:14 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 70DA13858031 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Corinna Vinschen To: cygwin-cvs@sourceware.org Subject: [newlib-cygwin] Cygwin: pipes: fix POSIX requirement for non-blocking pipe writes X-Act-Checkin: newlib-cygwin X-Git-Author: Corinna Vinschen X-Git-Refname: refs/heads/master X-Git-Oldrev: fcccc4b74308fa2dc57191b82028c69ca0d4b5c7 X-Git-Newrev: a5b2c735e6f4968158d286740e127187abde5e50 Message-Id: <20210914150614.70DA13858031@sourceware.org> Date: Tue, 14 Sep 2021 15:06:14 +0000 (GMT) X-BeenThere: cygwin-cvs@cygwin.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Cygwin core component git logs List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 14 Sep 2021 15:06:14 -0000 https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;h=a5b2c735e6f4968158d286740e127187abde5e50 commit a5b2c735e6f4968158d286740e127187abde5e50 Author: Corinna Vinschen Date: Fri Sep 3 10:45:49 2021 +0200 Cygwin: pipes: fix POSIX requirement for non-blocking pipe writes 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 Diff: --- winsup/cygwin/fhandler_pipe.cc | 51 ++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/winsup/cygwin/fhandler_pipe.cc b/winsup/cygwin/fhandler_pipe.cc index b69bb1d40..06d989772 100644 --- a/winsup/cygwin/fhandler_pipe.cc +++ b/winsup/cygwin/fhandler_pipe.cc @@ -313,7 +313,6 @@ fhandler_pipe::raw_read (void *ptr, size_t& len) ssize_t __reg3 fhandler_pipe::raw_write (const void *ptr, size_t len) { - ssize_t ret = -1; size_t nbytes = 0; ULONG chunk; NTSTATUS status = STATUS_SUCCESS; @@ -351,9 +350,36 @@ fhandler_pipe::raw_write (const void *ptr, size_t len) len1 = chunk; else len1 = (ULONG) left; - nbytes_now = 0; - status = NtWriteFile (get_handle (), evt, NULL, NULL, &io, - (PVOID) ptr, len1, NULL, NULL); + /* 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. + + POSIX requires + - A write request for {PIPE_BUF} or fewer bytes shall have the + following effect: if there is sufficient space available in the + pipe, write() shall transfer all the data and return the number + of bytes requested. Otherwise, write() shall transfer no data and + return -1 with errno set to [EAGAIN]. + + - A write request for more than {PIPE_BUF} bytes shall cause one + of the following: + + - When at least one byte can be written, transfer what it can and + return the number of bytes written. When all data previously + written to the pipe is read, it shall transfer at least {PIPE_BUF} + bytes. + + - When no data can be written, transfer no data, and return -1 with + errno set to [EAGAIN]. */ + while (len1 > 0) + { + status = NtWriteFile (get_handle (), evt, NULL, NULL, &io, + (PVOID) ptr, len1, NULL, NULL); + if (evt || !NT_SUCCESS (status) || io.Information > 0 + || len <= PIPE_BUF) + break; + len1 >>= 1; + } if (evt && status == STATUS_PENDING) { waitret = cygwait (evt); @@ -375,13 +401,11 @@ fhandler_pipe::raw_write (const void *ptr, size_t len) else 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) + nbytes_now; nbytes += nbytes_now; + /* 0 bytes returned? EAGAIN. See above. */ + if (nbytes == 0) + set_errno (EAGAIN); } else if (STATUS_PIPE_IS_CLOSED (status)) { @@ -392,17 +416,16 @@ fhandler_pipe::raw_write (const void *ptr, size_t len) __seterrno_from_nt_status (status); if (nbytes_now == 0) - len = 0; /* Terminate loop. */ - if (nbytes > 0) - ret = nbytes; + break; } if (evt) CloseHandle (evt); - if (status == STATUS_THREAD_SIGNALED && ret < 0) + if (status == STATUS_THREAD_SIGNALED && nbytes == 0) set_errno (EINTR); else if (status == STATUS_THREAD_CANCELED) pthread::static_cancel_self (); - return ret; + return nbytes ?: -1; +} } int