From: Bruno Haible Date: Thu, 14 Apr 2011 21:42:01 +0000 (+0200) Subject: Support non-blocking pipe I/O in write() on native Windows. X-Git-Tag: v0.1~2943 X-Git-Url: http://erislabs.net/gitweb/?a=commitdiff_plain;h=73899ad375e2b795e1caaac9a75b99bc87cd9aea;p=gnulib.git Support non-blocking pipe I/O in write() on native Windows. * lib/write.c (rpl_write): Split a write request that failed merely because the byte count was larger than the pipe buffer's size. * doc/posix-functions/write.texi: Mention the problem with large byte counts. --- diff --git a/ChangeLog b/ChangeLog index 349e5fc77..1657a3466 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2011-04-14 Bruno Haible + Support non-blocking pipe I/O in write() on native Windows. + * lib/write.c (rpl_write): Split a write request that failed merely + because the byte count was larger than the pipe buffer's size. + * doc/posix-functions/write.texi: Mention the problem with large byte + counts. + +2011-04-14 Bruno Haible + wchar: Ensure that wchar_t gets defined on uClibc. * lib/wchar.in.h: On uClibc, include . Reported by Giuseppe Scrivano . diff --git a/doc/posix-functions/write.texi b/doc/posix-functions/write.texi index 7587fbb9d..604507d30 100644 --- a/doc/posix-functions/write.texi +++ b/doc/posix-functions/write.texi @@ -13,6 +13,12 @@ When writing to a non-blocking pipe whose buffer is full, this function fails with @code{errno} being set to @code{ENOSPC} instead of @code{EAGAIN} on some platforms: mingw. +@item +When writing to a non-blocking pipe on which no reader is currently waiting +an amount of bytes that exceeds the pipe buffer's size, then -- even if the +pipe's buffer is empty -- this function fails, instead of performing a partial +write into the pipe buffer, on some platforms: +mingw. @end itemize Portability problems fixed by Gnulib module @code{stdio}, together with module @code{sigpipe}: diff --git a/lib/write.c b/lib/write.c index 4b40cd31d..b0ffa94e9 100644 --- a/lib/write.c +++ b/lib/write.c @@ -42,42 +42,81 @@ ssize_t rpl_write (int fd, const void *buf, size_t count) #undef write { - ssize_t ret = write (fd, buf, count); - - if (ret < 0) + for (;;) { -# if GNULIB_NONBLOCKING - if (errno == ENOSPC) + ssize_t ret = write (fd, buf, count); + + if (ret < 0) { - HANDLE h = (HANDLE) _get_osfhandle (fd); - if (GetFileType (h) == FILE_TYPE_PIPE) +# if GNULIB_NONBLOCKING + if (errno == ENOSPC) { - /* h is a pipe or socket. */ - DWORD state; - if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, NULL, 0) - && (state & PIPE_NOWAIT) != 0) - /* h is a pipe in non-blocking mode. - Change errno from ENOSPC to EAGAIN. */ - errno = EAGAIN; + HANDLE h = (HANDLE) _get_osfhandle (fd); + if (GetFileType (h) == FILE_TYPE_PIPE) + { + /* h is a pipe or socket. */ + DWORD state; + if (GetNamedPipeHandleState (h, &state, NULL, NULL, NULL, + NULL, 0) + && (state & PIPE_NOWAIT) != 0) + { + /* h is a pipe in non-blocking mode. + We can get here in four situations: + 1. When the pipe buffer is full. + 2. When count <= pipe_buf_size and the number of + free bytes in the pipe buffer is < count. + 3. When count > pipe_buf_size and the number of free + bytes in the pipe buffer is > 0, < pipe_buf_size. + 4. When count > pipe_buf_size and the pipe buffer is + entirely empty. + The cases 1 and 2 are POSIX compliant. In cases 3 and + 4 POSIX specifies that write() must split the request + and succeed with a partial write. We fix case 4. + We don't fix case 3 because it is not essential for + programs. */ + DWORD out_size; /* size of the buffer for outgoing data */ + DWORD in_size; /* size of the buffer for incoming data */ + if (GetNamedPipeInfo (h, NULL, &out_size, &in_size, NULL)) + { + size_t reduced_count = count; + /* In theory we need only one of out_size, in_size. + But I don't know which of the two. The description + is ambiguous. */ + if (out_size != 0 && out_size < reduced_count) + reduced_count = out_size; + if (in_size != 0 && in_size < reduced_count) + reduced_count = in_size; + if (reduced_count < count) + { + /* Attempt to write only the first part. */ + count = reduced_count; + continue; + } + } + /* Change errno from ENOSPC to EAGAIN. */ + errno = EAGAIN; + } + } } - } - else + else # endif - { -# if GNULIB_SIGPIPE - if (GetLastError () == ERROR_NO_DATA - && GetFileType ((HANDLE) _get_osfhandle (fd)) == FILE_TYPE_PIPE) { - /* Try to raise signal SIGPIPE. */ - raise (SIGPIPE); - /* If it is currently blocked or ignored, change errno from - EINVAL to EPIPE. */ - errno = EPIPE; - } +# if GNULIB_SIGPIPE + if (GetLastError () == ERROR_NO_DATA + && GetFileType ((HANDLE) _get_osfhandle (fd)) + == FILE_TYPE_PIPE) + { + /* Try to raise signal SIGPIPE. */ + raise (SIGPIPE); + /* If it is currently blocked or ignored, change errno from + EINVAL to EPIPE. */ + errno = EPIPE; + } # endif + } } + return ret; } - return ret; } # endif