Test case:

#include <cstdlib>
#include <iostream>
#include <unistd.h>
#include <string.h>

int
main(int argc, char * argv[])
{
  if (argc != 2)
    {
      std::cerr << "Usage: ./test writer | ./test reader" << std::endl;
      return 2;
    }

  if (strcmp("reader", argv[1]) == 0)
    {
      // note, no input/output is allowed to happen before this call.
      std::cin.sync_with_stdio(false);
      int length = 0;
      while (std::cin.good())
        {
          char buffer[BUFSIZ];
          (void)std::cin.read(buffer, sizeof(buffer));
          length += std::cin.gcount();
        }

      if (length == 82)
        {
          std::cerr << "Success" << std::endl;
          return EXIT_SUCCESS;
        }
      else
        {
          std::cerr << "Failure, length is " << length << std::endl;
          return EXIT_FAILURE;
          }
    }
  else if (strcmp("writer", argv[1]) == 0)
    {
      std::cout << "1234567890123456789012345678901234567890" << std::endl;
      sleep(1);
      std::cout << "1234567890123456789012345678901234567890" << std::endl;
      return EXIT_SUCCESS;
    }
  else
    {
      std::cerr << "Bad argument" << std::endl;
      return 3;
    }
}

Analysis:

When sync_with_stdio(false), cin uses basic_filebuf which is based on
__basic_file, and its xsgetn function:

  streamsize 
  __basic_file<char>::xsgetn(char* __s, streamsize __n)
  {
    streamsize __ret;
    do
      __ret = read(this->fd(), __s, __n);
    while (__ret == -1L && errno == EINTR);
    return __ret;
  }

When fd() refers to a pipe, this function may return before __n characters are
read, but also before an end-of-file is detected (say if the pipe supplier just
isn't finished yet).  For the Posix read function this is a normal operating
mode.  However for one client of this function (basic_filebuf::xsgetn, called
by istream::read), if less than __n characters are returned, this represents an
error, subsequently setting failbit|eofbit in the istream.

The fix is to change  __basic_file<char>::xsgetn to hang in there until either
EOF is detected, or until there is a non-recoverable error.  I believe the
following rewrite will do the job:

  streamsize 
  __basic_file<char>::xsgetn(char* __s, streamsize __n)
  {
    streamsize __ret = 0;
    for (streamsize __i; __n > 0; __ret += __i, __s += __i, __n -= __i)
    {
       __i = read(this->fd(), __s, __n);
       if (__i == 0)
         break;
       if (__i == -1)
       {
         if (!(errno == EINTR || errno == EAGAIN))
           break;
         __i = 0;
       }
    }
    return __ret;
  }


-- 
           Summary: __basic_file<char>::xsgetn does not deal well with pipes
           Product: gcc
           Version: 4.0.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
        AssignedTo: unassigned at gcc dot gnu dot org
        ReportedBy: hhinnant at apple dot com
  GCC host triplet: Mac OS 10.4.5


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=27168

Reply via email to