bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: writing to non-blocking pipes on Windows


From: Bruno Haible
Subject: Re: writing to non-blocking pipes on Windows
Date: Thu, 14 Apr 2011 23:56:07 +0200
User-agent: KMail/1.9.9

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  <address@hidden>

        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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]