Bug 27168 - __basic_file<char>::xsgetn does not deal well with pipes
Summary: __basic_file<char>::xsgetn does not deal well with pipes
Status: RESOLVED DUPLICATE of bug 21286
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 4.0.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2006-04-14 18:48 UTC by Howard Hinnant
Modified: 2006-04-14 19:31 UTC (History)
4 users (show)

See Also:
Host: Mac OS 10.4.5
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Howard Hinnant 2006-04-14 18:48:50 UTC
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;
  }
Comment 1 Paolo Carlini 2006-04-14 19:31:42 UTC
Howard, this has been fixed in FSF 4.0.1, indeed, I cannot reproduce with anything >= 4.0.1. Thanks, anyway.

*** This bug has been marked as a duplicate of 21286 ***