On mingw, there was another test failure. Apparently the behaviour of write() on a non-blocking pipe fd depends on whether some pipe reader is currently blocked, read()ing from it. - If yes, then write() of more than the pipe buffer's size yield a partial write, no failure. - If no, then write() of more than the pipe buffer's size fails, with errno ENOSPC, _even_ if the pipe buffer is empty.
Example of the first case: read(70000) blocking, then write (140000) -> 70000. The next write (70000) -> -1 / ENOSPC. Example of the second case: A pipe reader exists but does a sleep(). Then write (140000) -> -1 / ENOSPC BUT write (4000) -> 4000. This patch adds a workaround. It is not possible to add a corresponding workaround to fwrite, fprintf, etc, but it is also not needed: Even on POSIX compliant systems, you cannot meaningfully use fprintf() on a non-blocking fd. Because when the output happens to be larger than the buffer size, fprintf will output the first part that fits in the buffer, then forget the rest and fail with errno = EAGAIN - which is not a useful behaviour. 2011-04-14 Bruno Haible <br...@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. *** doc/posix-functions/write.texi.orig Thu Apr 14 23:38:34 2011 --- doc/posix-functions/write.texi Thu Apr 14 23:07:36 2011 *************** *** 13,18 **** --- 13,24 ---- 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}: *** lib/write.c.orig Thu Apr 14 23:38:34 2011 --- lib/write.c Thu Apr 14 23:32:13 2011 *************** *** 42,83 **** rpl_write (int fd, const void *buf, size_t count) #undef write { ! ssize_t ret = write (fd, buf, count); ! ! if (ret < 0) { ! # if GNULIB_NONBLOCKING ! if (errno == ENOSPC) { ! 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. ! 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 } } - return ret; } # endif --- 42,122 ---- rpl_write (int fd, const void *buf, size_t count) #undef write { ! for (;;) { ! ssize_t ret = write (fd, buf, count); ! ! if (ret < 0) { ! # if GNULIB_NONBLOCKING ! if (errno == ENOSPC) { ! 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 + } } + return ret; } } # endif