Mercurial > hg > octave-shane > gnulib-hg
changeset 14582:3a41e32d28ab
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.
author | Bruno Haible <bruno@clisp.org> |
---|---|
date | Thu, 14 Apr 2011 23:42:01 +0200 |
parents | a1d4fafd50b6 |
children | 8b22057e98d2 |
files | ChangeLog doc/posix-functions/write.texi lib/write.c |
diffstat | 3 files changed, 83 insertions(+), 30 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2011-04-14 Bruno Haible <bruno@clisp.org> + + 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 <bruno@clisp.org> wchar: Ensure that wchar_t gets defined on uClibc.
--- a/doc/posix-functions/write.texi +++ b/doc/posix-functions/write.texi @@ -13,6 +13,12 @@ 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}:
--- a/lib/write.c +++ b/lib/write.c @@ -42,42 +42,81 @@ rpl_write (int fd, const void *buf, size_t count) #undef write { - ssize_t ret = write (fd, buf, count); + for (;;) + { + ssize_t ret = write (fd, buf, count); - if (ret < 0) - { -# if GNULIB_NONBLOCKING - if (errno == ENOSPC) + 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 +# 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; + } +# endif } } - 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; - } -# endif - } + return ret; } - return ret; } # endif